From 2f2c115e08351dea59b7df8c2fa2d1eaf7205273 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Tue, 15 Aug 2006 20:37:04 +0000 Subject: [PATCH] Initial import --- AUTHORS | 5 + COPYING | 280 ++++++++ COPYRIGHT | 18 + Makefile.am | 192 ++++++ acinclude.m4 | 37 ++ bootstrap | 6 + configure.ac | 103 +++ doc/Makefile | 4 + doc/comments | 52 ++ doc/doxygen.conf.in | 186 ++++++ libburn-1.pc.in | 11 + libburn/Makefile | 4 + libburn/Makefile.am | 65 ++ libburn/async.c | 191 ++++++ libburn/async.h | 8 + libburn/crc.c | 122 ++++ libburn/crc.h | 9 + libburn/debug.c | 35 + libburn/debug.h | 8 + libburn/drive.c | 369 +++++++++++ libburn/drive.h | 53 ++ libburn/error.h | 8 + libburn/file.c | 187 ++++++ libburn/file.h | 22 + libburn/init.c | 33 + libburn/init.h | 8 + libburn/lec.c | 451 +++++++++++++ libburn/lec.h | 12 + libburn/libburn.h | 928 ++++++++++++++++++++++++++ libburn/message.c | 108 +++ libburn/message.h | 19 + libburn/mmc.c | 506 ++++++++++++++ libburn/mmc.h | 37 ++ libburn/null.c | 27 + libburn/null.h | 10 + libburn/options.c | 169 +++++ libburn/options.h | 78 +++ libburn/read.c | 264 ++++++++ libburn/read.h | 14 + libburn/sbc.c | 40 ++ libburn/sbc.h | 11 + libburn/sector.c | 649 ++++++++++++++++++ libburn/sector.h | 31 + libburn/sg.c | 334 ++++++++++ libburn/sg.h | 19 + libburn/source.c | 36 + libburn/source.h | 8 + libburn/spc.c | 405 ++++++++++++ libburn/spc.h | 25 + libburn/structure.c | 305 +++++++++ libburn/structure.h | 70 ++ libburn/toc.c | 130 ++++ libburn/toc.h | 48 ++ libburn/transport.h | 162 +++++ libburn/util.c | 44 ++ libburn/util.h | 8 + libburn/write.c | 529 +++++++++++++++ libburn/write.h | 25 + libisofs-1.pc.in | 11 + libisofs/Makefile | 4 + libisofs/Makefile.am | 44 ++ libisofs/ecma119.c | 1506 ++++++++++++++++++++++++++++++++++++++++++ libisofs/ecma119.h | 220 ++++++ libisofs/errors.c | 14 + libisofs/errors.h | 19 + libisofs/libisofs.h | 243 +++++++ libisofs/rockridge.c | 299 +++++++++ libisofs/rockridge.h | 30 + libisofs/struct.c | 340 ++++++++++ libisofs/struct.h | 77 +++ libisofs/susp.c | 312 +++++++++ libisofs/susp.h | 75 +++ libisofs/test.c | 138 ++++ libisofs/tree.c | 409 ++++++++++++ libisofs/tree.h | 259 ++++++++ libisofs/util.c | 710 ++++++++++++++++++++ libisofs/util.h | 156 +++++ libisofs/volume.c | 72 ++ libisofs/volume.h | 55 ++ libisofs/writer.c | 1178 +++++++++++++++++++++++++++++++++ libisofs/writer.h | 103 +++ test/Makefile | 4 + test/blank.c | 100 +++ test/burn.c | 4 + test/burniso.c | 147 +++++ test/devices.c | 39 ++ test/iso.c | 138 ++++ test/iso.py | 297 +++++++++ test/master.c | 126 ++++ test/poll.c | 78 +++ test/rip.c | 54 ++ test/structest.c | 48 ++ test/toc.c | 103 +++ test/tree.py | 77 +++ test/tree.pyc | Bin 0 -> 3547 bytes version.h.in | 3 + 96 files changed, 15010 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 COPYRIGHT create mode 100644 Makefile.am 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/doxygen.conf.in create mode 100644 libburn-1.pc.in create mode 100644 libburn/Makefile create mode 100644 libburn/Makefile.am create mode 100644 libburn/async.c create mode 100644 libburn/async.h create mode 100644 libburn/crc.c create mode 100644 libburn/crc.h create mode 100644 libburn/debug.c create mode 100644 libburn/debug.h create mode 100644 libburn/drive.c create mode 100644 libburn/drive.h create mode 100644 libburn/error.h create mode 100644 libburn/file.c create mode 100644 libburn/file.h create mode 100644 libburn/init.c create mode 100644 libburn/init.h create mode 100644 libburn/lec.c create mode 100644 libburn/lec.h create mode 100644 libburn/libburn.h create mode 100644 libburn/message.c create mode 100644 libburn/message.h create mode 100644 libburn/mmc.c create mode 100644 libburn/mmc.h create mode 100644 libburn/null.c create mode 100644 libburn/null.h create mode 100644 libburn/options.c create mode 100644 libburn/options.h create mode 100644 libburn/read.c create mode 100644 libburn/read.h create mode 100644 libburn/sbc.c create mode 100644 libburn/sbc.h create mode 100644 libburn/sector.c create mode 100644 libburn/sector.h create mode 100644 libburn/sg.c create mode 100644 libburn/sg.h create mode 100644 libburn/source.c create mode 100644 libburn/source.h create mode 100644 libburn/spc.c create mode 100644 libburn/spc.h create mode 100644 libburn/structure.c create mode 100644 libburn/structure.h create mode 100644 libburn/toc.c create mode 100644 libburn/toc.h create mode 100644 libburn/transport.h create mode 100644 libburn/util.c create mode 100644 libburn/util.h create mode 100644 libburn/write.c create mode 100644 libburn/write.h create mode 100644 libisofs-1.pc.in create mode 100755 libisofs/Makefile create mode 100755 libisofs/Makefile.am create mode 100755 libisofs/ecma119.c create mode 100755 libisofs/ecma119.h create mode 100755 libisofs/errors.c create mode 100755 libisofs/errors.h create mode 100755 libisofs/libisofs.h create mode 100755 libisofs/rockridge.c create mode 100755 libisofs/rockridge.h create mode 100755 libisofs/struct.c create mode 100755 libisofs/struct.h create mode 100755 libisofs/susp.c create mode 100755 libisofs/susp.h create mode 100755 libisofs/test.c 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 100755 libisofs/writer.c create mode 100755 libisofs/writer.h create mode 100644 test/Makefile create mode 100644 test/blank.c create mode 100644 test/burn.c create mode 100644 test/burniso.c create mode 100644 test/devices.c create mode 100644 test/iso.c create mode 100644 test/iso.py create mode 100644 test/master.c create mode 100644 test/poll.c create mode 100644 test/rip.c create mode 100644 test/structest.c create mode 100644 test/toc.c create mode 100644 test/tree.py create mode 100644 test/tree.pyc create mode 100644 version.h.in diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..aa70292 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +Developers: + +Mario Đanić +Luke Biddell +Anant Narayanan 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..070ee8c --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,18 @@ +Derek Foreman and Ben Jansens +Copyright (C) 2002-2004 Derek Foreman and Ben Jansens +Mario Đanić , Luke Biddell , Anant Narayanan +Copyright (C) 2006 Mario Đanić, Luke Biddell, Anant Narayanan + + 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/Makefile.am b/Makefile.am new file mode 100644 index 0000000..be0e1f4 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,192 @@ +pkgconfigdir=$(libdir)/pkgconfig +libincludedir=$(includedir)/libburn/@BURN_MAJOR_VERSION@/libburn + +lib_LTLIBRARIES = libburn/libburn.la libisofs/libisofs.la + +## ========================================================================= ## + +# Build libraries +libburn_libburn_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) +libburn_libburn_la_SOURCES = \ + libburn/async.c \ + libburn/async.h \ + libburn/crc.c \ + libburn/crc.h \ + libburn/debug.c \ + libburn/debug.h \ + libburn/drive.c \ + libburn/drive.h \ + libburn/error.h \ + libburn/file.c \ + libburn/file.h \ + libburn/init.c \ + libburn/init.h \ + libburn/lec.c \ + libburn/lec.h \ + libburn/libburn.h \ + libburn/message.c \ + libburn/message.h \ + libburn/mmc.c \ + libburn/mmc.h \ + libburn/null.c \ + libburn/null.h \ + libburn/options.c \ + libburn/options.h \ + libburn/read.c \ + libburn/read.h \ + libburn/sbc.c \ + libburn/sbc.h \ + libburn/sector.c \ + libburn/sector.h \ + libburn/sg.c \ + libburn/sg.h \ + libburn/source.h \ + libburn/source.c \ + libburn/spc.c \ + libburn/spc.h \ + libburn/structure.c \ + libburn/structure.h \ + libburn/toc.c \ + libburn/toc.h \ + libburn/transport.h \ + libburn/util.c \ + libburn/util.h \ + libburn/write.c \ + libburn/write.h \ + version.h + +libisofs_libisofs_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) +libisofs_libisofs_la_SOURCES = \ + libisofs/errors.h \ + libisofs/errors.c \ + 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/struct.h \ + libisofs/struct.c \ + libisofs/susp.h \ + libisofs/susp.c \ + libisofs/rockridge.h \ + libisofs/rockridge.c \ + libisofs/libisofs.h + +libinclude_HEADERS = \ + libburn/libburn.h \ + libisofs/libisofs.h + +## ========================================================================= ## + +## Build test applications +noinst_PROGRAMS = \ + test/blank \ + test/burn \ + test/burniso \ + test/devices \ + test/iso \ + test/master \ + test/poll \ + test/rip \ + test/toc \ + test/structest + +test_devices_CPPFLAGS = -Ilibburn +test_devices_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_devices_SOURCES = test/devices.c +test_poll_CPPFLAGS = -Ilibburn +test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_poll_SOURCES = test/poll.c +test_toc_CPPFLAGS = -Ilibburn +test_toc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_toc_SOURCES = test/toc.c +test_rip_CPPFLAGS = -Ilibburn +test_rip_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_rip_SOURCES = test/rip.c +test_burn_CPPFLAGS = -Ilibburn +test_burn_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_burn_SOURCES = test/burn.c +test_burniso_CPPFLAGS = -Ilibburn +test_burniso_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_burniso_SOURCES = test/burniso.c +test_master_CPPFLAGS = -Ilibburn +test_master_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_master_SOURCES = test/master.c +test_structest_CPPFLAGS = -Ilibburn +test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_structest_SOURCES = test/structest.c +test_blank_CPPFLAGS = -Ilibburn +test_blank_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) +test_blank_SOURCES = test/blank.c +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 = / + +docdeps = \ + doc/doxygen.conf \ + doc/comments \ + libburn/libburn.h \ + libisofs/libisofs.h \ + test/burniso.c + +doc: doc/html + +doc/html: $(docdeps) + $(RM) -r $@ + doxygen doc/doxygen.conf + +doc-upload: doc/html + scp -r $ $1/$3" + old="$srcdir/$1/$2/$3" + new="$srcdir/$1/$3" + cat >$new <<__EOF__ +/* WARNING: This file was automatically generated! + * Original: $old + */ +__EOF__ + cat >>$new <$old +]) + +AC_DEFUN([TARGET_SHIZZLE], +[ + ARCH="" + + AC_MSG_CHECKING([target operating system]) + + case $target in + *-*-linux*) + ARCH=linux + ;; + *) + AC_ERROR([You are attempting to compile for an unsupported platform]) + ;; + esac + + AC_MSG_RESULT([$ARCH]) + + # this doesn't actually do anything yet.. but it will someday when we port + # libburn + + #COPY_ARCH_SRC(libburn, $ARCH, transport.c) +]) diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..5c2990b --- /dev/null +++ b/bootstrap @@ -0,0 +1,6 @@ +#!/bin/sh -x + +aclocal-1.7 +libtoolize --copy --force +autoconf +automake-1.7 --foreign --add-missing --copy --include-deps diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..100ac50 --- /dev/null +++ b/configure.ac @@ -0,0 +1,103 @@ +AC_INIT([libburn], [0.2.1], [http://libburn.pykix.org]) +AC_PREREQ([2.50]) +dnl AC_CONFIG_HEADER([config.h]) + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([subdir-objects]) + +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=1 +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 +if test ! $ac_cv_func_fseeko; then + AC_ERROR([Libburn requires largefile support.]) +fi + +AC_PROG_LIBTOOL +AC_SUBST(LIBTOOL_DEPS) +LIBTOOL="$LIBTOOL --silent" + +AC_PROG_INSTALL + +AC_CHECK_HEADERS() + +THREAD_LIBS=-lpthread +AC_SUBST(THREAD_LIBS) + +TARGET_SHIZZLE + +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 + libburn-1.pc + 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..a107f44 --- /dev/null +++ b/doc/comments @@ -0,0 +1,52 @@ +/** + @author Dana Jansens, Derek Foreman + + @mainpage Libburn Documentation Index + + @section intro Introduction + + Libburn is an open source library suite for reading, mastering and writing + optical discs. + + @section using Using the library + + @subsection concepts Library concepts + + There are a few concepts to introduce in order for you to understand how to + use Libburn. So, lets start with them: + +
    +
  1. Read - The process of reading the data on a disc for storage or + copying. +
  2. Write - The process of writing data onto a disc to create a new + disc image or append to an existing one. +
  3. Drive - A Drive is a hardware device used to reading and/or writing + discs. CD burners and CD-ROMs are examples of Drives. +
+ + @subsection working Working with the library + + Now that you understand the above @ref concepts, you're ready to look at the + actual use of the library. + + In general, using the library to perform a process consists of the following + steps: + +
    +
  1. Initialize the library. (If not already done; a single instance of + the library can perform multiple operations simultaneously with + multiple drives.) +
  2. Scan for available Drives. +
  3. Choose and grab a Drive for reading/writing. +
  4. Fill in the options for the operation. +
  5. Wait for the operation to complete, displaying status along the way + if desired. +
  6. Release the Drive. +
  7. Destroy the library instance. (If you're done working with the + library.) +
+ + Here's a very simple example of burning an ISO file + + @include burniso.c +*/ diff --git a/doc/doxygen.conf.in b/doc/doxygen.conf.in new file mode 100644 index 0000000..2f5ab15 --- /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 = libburn libisofs doc +FILE_PATTERNS = libburn.h 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/libburn-1.pc.in b/libburn-1.pc.in new file mode 100644 index 0000000..6a10a5c --- /dev/null +++ b/libburn-1.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libburn +Description: Disc reading/writing library +Version: @VERSION@ +Requires: +Libs: -L${libdir} -lburn @THREAD_LIBS@ +Cflags: -I${includedir}/libburn/@BURN_MAJOR_VERSION@ diff --git a/libburn/Makefile b/libburn/Makefile new file mode 100644 index 0000000..062350d --- /dev/null +++ b/libburn/Makefile @@ -0,0 +1,4 @@ +all clean: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean diff --git a/libburn/Makefile.am b/libburn/Makefile.am new file mode 100644 index 0000000..6301aa8 --- /dev/null +++ b/libburn/Makefile.am @@ -0,0 +1,65 @@ +pkgconfigdir=$(libdir)/pkgconfig +libincludedir=$(includedir)/libburn/@BURN_MAJOR_VERSION@ + +lib_LTLIBRARIES = libburn.la + +libburn_la_SOURCES = \ + async.c \ + async.h \ + crc.c \ + crc.h \ + debug.c \ + debug.h \ + drive.c \ + drive.h \ + file.c \ + file.h \ + init.c \ + init.h \ + lec.c \ + lec.h \ + message.c \ + message.h \ + mmc.c \ + mmc.h \ + null.c \ + null.h \ + options.c \ + options.h \ + read.c \ + read.h \ + sbc.c \ + sbc.h \ + sector.c \ + sector.h \ + sg.c \ + sg.h \ + spc.c \ + spc.h \ + source.h \ + source.c \ + structure.c \ + structure.h \ + toc.c \ + toc.h \ + transport.h \ + util.c \ + util.h \ + write.c \ + write.h + +libinclude_HEADERS = libburn.h + +## ========================================================================= ## +indent_files = $(libburn_la_SOURCES) + +indent: $(indent_files) + indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \ + -cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \ + -lp -saf -sai -nprs -npsl -saw -sob -ss -ut \ + -sbi0 -nsc -ts8 -npcs -ncdb -fca \ + $^ + +.PHONY: indent + +## ========================================================================= ## diff --git a/libburn/async.c b/libburn/async.c new file mode 100644 index 0000000..0e01f54 --- /dev/null +++ b/libburn/async.c @@ -0,0 +1,191 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "libburn.h" +#include "transport.h" +#include "drive.h" +#include "write.h" +#include "options.h" +#include "async.h" + +#include +#include +#include + +#define SCAN_GOING() (workers && !workers->drive) + +typedef void *(*WorkerFunc) (void *); + +struct scan_opts +{ + struct burn_drive_info **drives; + unsigned int *n_drives; + + int done; +}; + +struct erase_opts +{ + struct burn_drive *drive; + int fast; +}; + +struct write_opts +{ + struct burn_drive *drive; + struct burn_write_opts *opts; + struct burn_disc *disc; +}; + +struct w_list +{ + struct burn_drive *drive; + pthread_t thread; + + struct w_list *next; + + union w_list_data + { + struct scan_opts scan; + struct erase_opts erase; + struct write_opts write; + } u; +}; + +static struct w_list *workers; + +static struct w_list *find_worker(struct burn_drive *d) +{ + struct w_list *a; + + for (a = workers; a; a = a->next) + if (a->drive == d) + return a; + return NULL; +} + +static void add_worker(struct burn_drive *d, WorkerFunc f, void *data) +{ + struct w_list *a; + struct w_list *tmp; + + a = malloc(sizeof(struct w_list)); + a->drive = d; + a->u = *(union w_list_data *)data; + + /* insert at front of the list */ + a->next = workers; + tmp = workers; + workers = a; + + if (d) + d->busy = BURN_DRIVE_SPAWNING; + + if (pthread_create(&a->thread, NULL, f, a)) { + free(a); + workers = tmp; + return; + } +} + +static void remove_worker(pthread_t th) +{ + struct w_list *a, *l = NULL; + + for (a = workers; a; l = a, a = a->next) + if (a->thread == th) { + if (l) + l->next = a->next; + else + workers = a->next; + free(a); + break; + } + assert(a != NULL); /* wasn't found.. this should not be possible */ +} + +static void *scan_worker_func(struct w_list *w) +{ + burn_drive_scan_sync(w->u.scan.drives, w->u.scan.n_drives); + w->u.scan.done = 1; + return NULL; +} + +int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives) +{ + struct scan_opts o; + int ret = 0; + + /* cant be anything working! */ + assert(!(workers && workers->drive)); + + if (!workers) { + /* start it */ + o.drives = drives; + o.n_drives = n_drives; + o.done = 0; + add_worker(NULL, (WorkerFunc) scan_worker_func, &o); + } else if (workers->u.scan.done) { + /* its done */ + ret = workers->u.scan.done; + remove_worker(workers->thread); + assert(workers == NULL); + } else { + /* still going */ + } + return ret; +} + +static void *erase_worker_func(struct w_list *w) +{ + burn_disc_erase_sync(w->u.erase.drive, w->u.erase.fast); + remove_worker(pthread_self()); + return NULL; +} + +void burn_disc_erase(struct burn_drive *drive, int fast) +{ + struct erase_opts o; + + assert(drive); + assert(!SCAN_GOING()); + assert(!find_worker(drive)); + + o.drive = drive; + o.fast = fast; + add_worker(drive, (WorkerFunc) erase_worker_func, &o); +} + +static void *write_disc_worker_func(struct w_list *w) +{ + burn_disc_write_sync(w->u.write.opts, w->u.write.disc); + + /* the options are refcounted, free out ref count which we added below + */ + burn_write_opts_free(w->u.write.opts); + + remove_worker(pthread_self()); + return NULL; +} + +void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc) +{ + struct write_opts o; + + assert(!SCAN_GOING()); + assert(!find_worker(opts->drive)); + o.drive = opts->drive; + o.opts = opts; + o.disc = disc; + + opts->refcount++; + + add_worker(opts->drive, (WorkerFunc) write_disc_worker_func, &o); +} + +void burn_async_join_all(void) +{ + void *ret; + + while (workers) + pthread_join(workers->thread, &ret); +} diff --git a/libburn/async.h b/libburn/async.h new file mode 100644 index 0000000..0e1d667 --- /dev/null +++ b/libburn/async.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__ASYNC_H +#define BURN__ASYNC_H + +void burn_async_join_all(void); +struct burn_write_opts; +#endif /* BURN__ASYNC_H */ diff --git a/libburn/crc.c b/libburn/crc.c new file mode 100644 index 0000000..fddc5b4 --- /dev/null +++ b/libburn/crc.c @@ -0,0 +1,122 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "crc.h" + +static unsigned short ccitt_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +unsigned long crc32_table[256] = { + 0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L, + 0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L, + 0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L, + 0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L, + 0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L, + 0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L, + 0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L, + 0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L, + 0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L, + 0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L, + 0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L, + 0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L, + 0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L, + 0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L, + 0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L, + 0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L, + 0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L, + 0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L, + 0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L, + 0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L, + 0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L, + 0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L, + 0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L, + 0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L, + 0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L, + 0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L, + 0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L, + 0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L, + 0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L, + 0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L, + 0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L, + 0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L, + 0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L, + 0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L, + 0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L, + 0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L, + 0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L, + 0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L, + 0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L, + 0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L, + 0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L, + 0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L, + 0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L, + 0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L, + 0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L, + 0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L, + 0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L, + 0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L, + 0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L, + 0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L, + 0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L, + 0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L, + 0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L, + 0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L, + 0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L, + 0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L, + 0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L, + 0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L, + 0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L, + 0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L, + 0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L, + 0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L, + 0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L, + 0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L +}; + +unsigned short crc_ccitt(unsigned char *q, int len) +{ + unsigned short crc = 0; + + while (len-- > 0) + crc = ccitt_table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8); + return ~crc; +} +unsigned int crc_32(unsigned char *data, int len) +{ + unsigned int crc = 0; + + while (len-- > 0) + crc = crc32_table[(crc ^ *data++) & 0xffL] ^ (crc >> 8); + return crc; +} diff --git a/libburn/crc.h b/libburn/crc.h new file mode 100644 index 0000000..a4846a3 --- /dev/null +++ b/libburn/crc.h @@ -0,0 +1,9 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__CRC_H +#define BURN__CRC_H + +unsigned short crc_ccitt(unsigned char *, int len); +unsigned int crc_32(unsigned char *, int len); + +#endif /* BURN__CRC_H */ diff --git a/libburn/debug.c b/libburn/debug.c new file mode 100644 index 0000000..b4abab7 --- /dev/null +++ b/libburn/debug.c @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifdef WIN32 +#include +#endif + +#include +#include +#include "libburn.h" +#include "debug.h" + +static int burn_verbosity = 0; + +void burn_set_verbosity(int v) +{ + burn_verbosity = v; +} + +void burn_print(int level, const char *a, ...) +{ +#ifdef WIN32 + char debug_string_data[256]; +#endif + va_list vl; + + if (level <= burn_verbosity) { + va_start(vl, a); +#ifdef WIN32 + vsprintf(debug_string_data, a, vl); + OutputDebugString(debug_string_data); +#else + vfprintf(stderr, a, vl); +#endif + } +} diff --git a/libburn/debug.h b/libburn/debug.h new file mode 100644 index 0000000..b566de0 --- /dev/null +++ b/libburn/debug.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__DEBUG_H +#define BURN__DEBUG_H + +void burn_print(int level, const char *a, ...); + +#endif /* BURN__DEBUG_H */ diff --git a/libburn/drive.c b/libburn/drive.c new file mode 100644 index 0000000..a96816a --- /dev/null +++ b/libburn/drive.c @@ -0,0 +1,369 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libburn.h" +#include "drive.h" +#include "transport.h" +#include "message.h" +#include "debug.h" +#include "init.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "structure.h" + +static struct burn_drive drive_array[255]; +static int drivetop = -1; + +void burn_drive_free(void) +{ + int i; + struct burn_drive *d; + + for (i = 0; i < drivetop + 1; i++) { + d = &drive_array[i]; + free((void *)d->idata); + free((void *)d->mdata); + free((void *)d->toc_entry); + free(d->devname); + } + drivetop = -1; + memset(drive_array, 0, sizeof(drive_array)); +} + +/* +void drive_read_lead_in(int dnum) +{ + mmc_read_lead_in(&drive_array[dnum], get_4k()); +} +*/ +unsigned int burn_drive_count(void) +{ + return drivetop + 1; +} + +int burn_drive_grab(struct burn_drive *d, int le) +{ + int errcode; + + if (!d->released) { + burn_print(1, "can't grab - already grabbed\n"); + return 0; + } + errcode = d->grab(d); + + if (errcode == 0) { + burn_print(1, "low level drive grab failed\n"); + return 0; + } + d->busy = BURN_DRIVE_GRABBING; + + if (le) + d->load(d); + + d->lock(d); + d->status = BURN_DISC_BLANK; + if (d->mdata->cdr_write || d->mdata->cdrw_write || + d->mdata->dvdr_write || d->mdata->dvdram_write) { + d->read_disc_info(d); + } else + d->read_toc(d); + d->busy = BURN_DRIVE_IDLE; + return 1; +} + +struct burn_drive *burn_drive_register(struct burn_drive *d) +{ + d->block_types[0] = 0; + d->block_types[1] = 0; + d->block_types[2] = 0; + d->block_types[3] = 0; + d->toc_temp = 0; + d->nwa = 0; + d->alba = 0; + d->rlba = 0; + d->cancel = 0; + d->busy = BURN_DRIVE_IDLE; + d->toc_entries = 0; + d->toc_entry = NULL; + d->disc = NULL; + d->erasable = 0; + memcpy(&drive_array[drivetop + 1], d, sizeof(struct burn_drive)); + pthread_mutex_init(&drive_array[drivetop + 1].access_lock, NULL); + return &drive_array[++drivetop]; +} + +void burn_drive_release(struct burn_drive *d, int le) +{ + if (d->released) + burn_print(1, "second release on drive!\n"); + assert(!d->busy); + if (le) + d->eject(d); + d->unlock(d); + + d->release(d); + + d->status = BURN_DISC_UNREADY; + d->released = 1; + if (d->toc_entry) + free(d->toc_entry); + d->toc_entry = NULL; + d->toc_entries = 0; + if (d->disc != NULL) { + burn_disc_free(d->disc); + d->disc = NULL; + } +} + +void burn_wait_all(void) +{ + unsigned int i; + int finished = 0; + struct burn_drive *d; + + while (!finished) { + finished = 1; + d = drive_array; + for (i = burn_drive_count(); i > 0; --i, ++d) { + assert(d->released); + } + if (!finished) + sleep(1); + } +} + +void burn_disc_erase_sync(struct burn_drive *d, int fast) +{ + burn_message_clear_queue(); + + burn_print(1, "erasing drive %s %s\n", d->idata->vendor, + d->idata->product); + + if (d->status != BURN_DISC_FULL) + return; + d->cancel = 0; + d->busy = BURN_DRIVE_ERASING; + d->erase(d, fast); + /* reset the progress */ + d->progress.session = 0; + d->progress.sessions = 1; + d->progress.track = 0; + d->progress.tracks = 1; + d->progress.index = 0; + d->progress.indices = 1; + d->progress.start_sector = 0; + d->progress.sectors = 0x10000; + d->progress.sector = 0; + /* read the initial 0 stage */ + while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0) + sleep(1); + while (!d->test_unit_ready(d) && + (d->progress.sector = d->get_erase_progress(d)) > 0) + sleep(1); + d->progress.sector = 0x10000; + d->busy = BURN_DRIVE_IDLE; +} + +enum burn_disc_status burn_disc_get_status(struct burn_drive *d) +{ + assert(!d->released); + return d->status; +} + +int burn_disc_erasable(struct burn_drive *d) +{ + return d->erasable; +} +enum burn_drive_status burn_drive_get_status(struct burn_drive *d, + struct burn_progress *p) +{ + if (p) { + memcpy(p, &(d->progress), sizeof(struct burn_progress)); + /* TODO: add mutex */ + } + return d->busy; +} + +void burn_drive_cancel(struct burn_drive *d) +{ + pthread_mutex_lock(&d->access_lock); + d->cancel = 1; + pthread_mutex_unlock(&d->access_lock); +} + +int burn_drive_get_block_types(struct burn_drive *d, + enum burn_write_types write_type) +{ + burn_print(12, "write type: %d\n", write_type); + assert( /* (write_type >= BURN_WRITE_PACKET) && */ + (write_type <= BURN_WRITE_RAW)); + return d->block_types[write_type]; +} + +static void strip_spaces(char *str) +{ + char *tmp; + + tmp = str + strlen(str) - 1; + while (isspace(*tmp)) + *(tmp--) = '\0'; + + tmp = str; + while (*tmp) { + if (isspace(*tmp) && isspace(*(tmp + 1))) { + char *tmp2; + + for (tmp2 = tmp + 1; *tmp2; ++tmp2) + *(tmp2 - 1) = *tmp2; + *(tmp2 - 1) = '\0'; + } else + ++tmp; + } +} + +static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out) +{ + struct scsi_inquiry_data *id; + + assert(d->idata); + assert(d->mdata); + if (!d->idata->valid || !d->mdata->valid) + return 0; + + id = (struct scsi_inquiry_data *)d->idata; + + memcpy(out->vendor, id->vendor, sizeof(id->vendor)); + strip_spaces(out->vendor); + memcpy(out->product, id->product, sizeof(id->product)); + strip_spaces(out->product); + memcpy(out->revision, id->revision, sizeof(id->revision)); + strip_spaces(out->revision); + strncpy(out->location, d->devname, 16); + out->location[16] = '\0'; + out->buffer_size = d->mdata->buffer_size; + out->read_dvdram = !!d->mdata->dvdram_read; + out->read_dvdr = !!d->mdata->dvdr_read; + out->read_dvdrom = !!d->mdata->dvdrom_read; + out->read_cdr = !!d->mdata->cdr_read; + out->read_cdrw = !!d->mdata->cdrw_read; + out->write_dvdram = !!d->mdata->dvdram_write; + out->write_dvdr = !!d->mdata->dvdr_write; + out->write_cdr = !!d->mdata->cdr_write; + out->write_cdrw = !!d->mdata->cdrw_write; + out->write_simulate = !!d->mdata->simulate; + out->c2_errors = !!d->mdata->c2_pointers; + out->drive = d; + /* update available block types for burners */ + if (out->write_dvdram || out->write_dvdr || + out->write_cdrw || out->write_cdr) + d->probe_write_modes(d); + out->tao_block_types = d->block_types[BURN_WRITE_TAO]; + out->sao_block_types = d->block_types[BURN_WRITE_SAO]; + out->raw_block_types = d->block_types[BURN_WRITE_RAW]; + out->packet_block_types = d->block_types[BURN_WRITE_PACKET]; + return 1; +} + +int burn_drive_scan_sync(struct burn_drive_info *drives[], + unsigned int *n_drives) +{ + /* state vars for the scan process */ + static int scanning = 0, scanned, found; + static unsigned num_scanned, count; + unsigned int i; + struct burn_drive *d; + + assert(burn_running); + + if (!scanning) { + scanning = 1; + /* make sure the drives aren't in use */ + burn_wait_all(); /* make sure the queue cleans up + before checking for the released + state */ + + d = drive_array; + count = burn_drive_count(); + for (i = 0; i < count; ++i, ++d) + assert(d->released == 1); + + /* refresh the lib's drives */ + sg_enumerate(); + ata_enumerate(); + count = burn_drive_count(); + if (count) + *drives = + malloc(sizeof(struct burn_drive_info) * count); + else + *drives = NULL; + *n_drives = scanned = found = num_scanned = 0; + } + + for (i = 0; i < count; ++i) { + if (scanned & (1 << i)) + continue; /* already scanned the device */ + + while (!drive_getcaps(&drive_array[i], + &(*drives)[num_scanned])) { + sleep(1); + } + scanned |= 1 << i; + found |= 1 << i; + num_scanned++; + (*n_drives)++; + } + + if (num_scanned == count) { + /* done scanning */ + scanning = 0; + return 1; + } + return 0; +} + +void burn_drive_info_free(struct burn_drive_info *info) +{ + free(info); + burn_drive_free(); +} + +struct burn_disc *burn_drive_get_disc(struct burn_drive *d) +{ + d->disc->refcnt++; + return d->disc; +} + +void burn_drive_set_speed(struct burn_drive *d, int r, int w) +{ + d->set_speed(d, r, w); +} + +int burn_msf_to_sectors(int m, int s, int f) +{ + return (m * 60 + s) * 75 + f; +} + +void burn_sectors_to_msf(int sectors, int *m, int *s, int *f) +{ + *m = sectors / (60 * 75); + *s = (sectors - *m * 60 * 75) / 75; + *f = sectors - *m * 60 * 75 - *s * 75; +} + +int burn_drive_get_read_speed(struct burn_drive *d) +{ + return d->mdata->max_read_speed; +} + +int burn_drive_get_write_speed(struct burn_drive *d) +{ + return d->mdata->max_write_speed; +} diff --git a/libburn/drive.h b/libburn/drive.h new file mode 100644 index 0000000..e6f7e27 --- /dev/null +++ b/libburn/drive.h @@ -0,0 +1,53 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __DRIVE +#define __DRIVE + +#include "libburn.h" +#include "toc.h" +#include "structure.h" + +struct burn_drive; +struct command; +struct mempage; + +#define LEAD_IN 1 +#define GAP 2 +#define USER_DATA 3 +#define LEAD_OUT 4 +#define SYNC 5 + +#define SESSION_LEADOUT_ENTRY(d,s) (d)->toc->session[(s)].leadout_entry + +#define CURRENT_SESSION_START(d) \ + burn_msf_to_lba(d->toc->session[d->currsession].start_m, \ + d->toc->session[d->currsession].start_s, \ + d->toc->session[d->currsession].start_f) + +#define SESSION_END(d,s) \ + TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (s))) + +#define PREVIOUS_SESSION_END(d) \ + TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (d)->currsession-1)) + +#define LAST_SESSION_END(d) \ + TOC_ENTRY_PLBA((d)->toc, \ + SESSION_LEADOUT_ENTRY((d), (d)->toc->sessions-1)) + +struct burn_drive *burn_drive_register(struct burn_drive *); + +unsigned int burn_drive_count(void); +void burn_wait_all(void); +int burn_sector_length_write(struct burn_drive *d); +int burn_track_control(struct burn_drive *d, int); +void burn_write_empty_sector(int fd); +void burn_write_empty_subcode(int fd); +void burn_drive_free(void); + +int burn_drive_scan_sync(struct burn_drive_info *drives[], + unsigned int *n_drives); +void burn_disc_erase_sync(struct burn_drive *d, int fast); +int burn_drive_get_block_types(struct burn_drive *d, + enum burn_write_types write_type); + +#endif /* __DRIVE */ diff --git a/libburn/error.h b/libburn/error.h new file mode 100644 index 0000000..74d4f68 --- /dev/null +++ b/libburn/error.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode; t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __ERROR_H +#define __ERROR_H + +#define BE_CANCELLED 1 + +#endif /* __ERROR_H */ diff --git a/libburn/file.c b/libburn/file.c new file mode 100644 index 0000000..809b842 --- /dev/null +++ b/libburn/file.c @@ -0,0 +1,187 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include "source.h" +#include "libburn.h" +#include "file.h" + +/* main channel data can be padded on read, but 0 padding the subs will make +an unreadable disc */ + + +/* This is a generic OS oriented function wrapper which compensates + shortcommings of read() in respect to a guaranteed amount of return data. + See man 2 read , paragraph "RETURN VALUE". + Possibly libburn/file.c is not the right storage location for this. + To make it ready for a move, this function is not declared static. +*/ +static int read_full_buffer(int fd, unsigned char *buffer, int size) +{ + int ret,summed_ret = 0; + + /* make safe against partial buffer returns */ + while (1) { + ret = read(fd, buffer + summed_ret, size - summed_ret); + if (ret <= 0) + break; + summed_ret += ret; + if (summed_ret >= size) + break; + } + if (ret < 0) /* error encountered. abort immediately */ + return ret; + return summed_ret; +} + + +static int file_read(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fd *fs = source->data; + + return read_full_buffer(fs->datafd, buffer, size); +} + +static int file_read_sub(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_file *fs = source->data; + + return read_full_buffer(fs->subfd, buffer, size); +} + +static void file_free(struct burn_source *source) +{ + struct burn_source_file *fs = source->data; + + close(fs->datafd); + if (source->read_sub) + close(fs->subfd); + free(fs); +} + +static off_t file_size(struct burn_source *source) +{ + struct stat buf; + struct burn_source_file *fs = source->data; + + if (fstat(fs->datafd, &buf) == -1) + return (off_t) 0; + /* for now we keep it compatible to the old (int) return value */ + if(buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */ + return (off_t) 1308622848; + return (off_t) buf.st_size; +} + +struct burn_source *burn_file_source_new(const char *path, const char *subpath) +{ + struct burn_source_file *fs; + struct burn_source *src; + int fd1, fd2 = 0; + + if (!path) + return NULL; + fd1 = open(path, O_RDONLY); + if (fd1 == -1) + return NULL; + if (subpath) { + fd2 = open(subpath, O_RDONLY); + if (fd2 == -1) { + close(fd1); + return NULL; + } + } + fs = malloc(sizeof(struct burn_source_file)); + fs->datafd = fd1; + + if (subpath) + fs->subfd = fd2; + + src = burn_source_new(); + src->read = file_read; + if (subpath) + src->read_sub = file_read_sub; + + src->get_size = file_size; + src->free_data = file_free; + src->data = fs; + return src; +} + + +/* ------ provisory location for the new source subclass fd --------- */ + +static off_t fd_get_size(struct burn_source *source) +{ + struct stat buf; + struct burn_source_fd *fs = source->data; + + if (fs->fixed_size > 0) + return fs->fixed_size; + if (fstat(fs->datafd, &buf) == -1) + return (off_t) 0; + /* for now we keep it compatible to the old (int) return value */ + if (buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */ + return (off_t) 1308622848; + return buf.st_size; +} + +static int fd_read(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fd *fs = source->data; + + return read_full_buffer(fs->datafd, buffer, size); +} + + +static int fd_read_sub(struct burn_source *source, + unsigned char *buffer, + int size) +{ + struct burn_source_fd *fs = source->data; + + return read_full_buffer(fs->subfd, buffer, size); +} + + +static void fd_free_data(struct burn_source *source) +{ + struct burn_source_fd *fs = source->data; + + close(fs->datafd); + if (source->read_sub) + close(fs->subfd); + free(fs); +} + + +struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size) +{ + struct burn_source_fd *fs; + struct burn_source *src; + + if (datafd == -1) + return NULL; + fs = malloc(sizeof(struct burn_source_fd)); + fs->datafd = datafd; + fs->subfd = subfd; + fs->fixed_size = size; + + src = burn_source_new(); + src->read = fd_read; + if(subfd != -1) + src->read = fd_read_sub; + src->get_size = fd_get_size; + src->free_data = fd_free_data; + src->data = fs; + return src; +} + diff --git a/libburn/file.h b/libburn/file.h new file mode 100644 index 0000000..96c1a69 --- /dev/null +++ b/libburn/file.h @@ -0,0 +1,22 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__FILE_H +#define BURN__FILE_H + +struct burn_source_file +{ + int datafd; + int subfd; +}; + + +/* ------ provisory location for the new source subclass fd --------- */ + +struct burn_source_fd +{ + int datafd; + int subfd; + off_t fixed_size; +}; + +#endif /* LIBBURN__FILE_H */ diff --git a/libburn/init.c b/libburn/init.c new file mode 100644 index 0000000..a6f3941 --- /dev/null +++ b/libburn/init.c @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include "init.h" +#include "sg.h" +#include "error.h" +#include "libburn.h" +#include "drive.h" + +int burn_running = 0; + +int burn_initialize(void) +{ + if (burn_running) + return 1; + + burn_running = 1; + return 1; +} + +void burn_finish(void) +{ + assert(burn_running); + + burn_wait_all(); + + burn_drive_free(); + + burn_running = 0; +} diff --git a/libburn/init.h b/libburn/init.h new file mode 100644 index 0000000..c3e9b9a --- /dev/null +++ b/libburn/init.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__INIT_H +#define BURN__INIT_H + +extern int burn_running; + +#endif /* BURN__INIT_H */ diff --git a/libburn/lec.c b/libburn/lec.c new file mode 100644 index 0000000..9141593 --- /dev/null +++ b/libburn/lec.c @@ -0,0 +1,451 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* borrowed HEAVILY from cdrdao */ + +#include +#include "lec.h" + +#define LEC_HEADER_OFFSET 12 +#define LEC_MODE1_P_PARITY_OFFSET 2076 +#define LEC_MODE1_Q_PARITY_OFFSET 2248 + +static unsigned char gf8_ilog[255] = { + 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, + 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, + 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, + 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, + 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, + 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, + 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, + 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, + 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, + 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, + 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, + 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, + 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, + 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, + 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, + 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, + 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, + 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, + 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, +}; +static unsigned char gf8_log[256] = { + 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, + 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, + 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, + 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, + 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, + 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, + 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, + 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, + 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, + 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, + 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, + 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, + 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, + 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, + 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, + 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, + 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, + 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, + 173, 232, 116, 214, 244, 234, 168, 80, 88, 175, +}; +static unsigned char gf8_q_coeffs[2][45] = { + {97, 251, 133, 60, 82, 160, 155, 201, 8, 112, 246, 11, 21, 42, 157, + 169, 80, 174, 232, 230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200, + 75, 103, 221, 252, 96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0}, + {190, 96, 250, 132, 59, 81, 159, 154, 200, 7, 111, 245, 10, 20, 41, + 156, 168, 79, 173, 231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8, + 199, 74, 102, 220, 251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0} +}; +static unsigned char gf8_p_coeffs[2][26] = { + {230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200, 75, 103, 221, 252, + 96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0}, + {231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8, 199, 74, 102, 220, + 251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0} +}; + +static unsigned char yellowbook_scrambler[2340] = { + 1, 128, 0, 96, 0, 40, 0, 30, 128, 8, 96, 6, 168, 2, 254, 129, 128, 96, + 96, 40, 40, 30, 158, + 136, 104, 102, 174, 170, 252, 127, 1, 224, 0, 72, 0, 54, 128, 22, 224, + 14, 200, 4, 86, 131, 126, 225, + 224, 72, 72, 54, 182, 150, 246, 238, 198, 204, 82, 213, 253, 159, 1, + 168, 0, 126, 128, 32, 96, 24, 40, + 10, 158, 135, 40, 98, 158, 169, 168, 126, 254, 160, 64, 120, 48, 34, + 148, 25, 175, 74, 252, 55, 1, 214, + 128, 94, 224, 56, 72, 18, 182, 141, 182, 229, 182, 203, 54, 215, 86, + 222, 190, 216, 112, 90, 164, 59, 59, + 83, 83, 125, 253, 225, 129, 136, 96, 102, 168, 42, 254, 159, 0, 104, 0, + 46, 128, 28, 96, 9, 232, 6, + 206, 130, 212, 97, 159, 104, 104, 46, 174, 156, 124, 105, 225, 238, + 200, 76, 86, 181, 254, 247, 0, 70, 128, + 50, 224, 21, 136, 15, 38, 132, 26, 227, 75, 9, 247, 70, 198, 178, 210, + 245, 157, 135, 41, 162, 158, 249, + 168, 66, 254, 177, 128, 116, 96, 39, 104, 26, 174, 139, 60, 103, 81, + 234, 188, 79, 49, 244, 20, 71, 79, + 114, 180, 37, 183, 91, 54, 187, 86, 243, 126, 197, 224, 83, 8, 61, 198, + 145, 146, 236, 109, 141, 237, 165, + 141, 187, 37, 179, 91, 53, 251, 87, 3, 126, 129, 224, 96, 72, 40, 54, + 158, 150, 232, 110, 206, 172, 84, + 125, 255, 97, 128, 40, 96, 30, 168, 8, 126, 134, 160, 98, 248, 41, 130, + 158, 225, 168, 72, 126, 182, 160, + 118, 248, 38, 194, 154, 209, 171, 28, 127, 73, 224, 54, 200, 22, 214, + 142, 222, 228, 88, 75, 122, 183, 99, + 54, 169, 214, 254, 222, 192, 88, 80, 58, 188, 19, 49, 205, 212, 85, + 159, 127, 40, 32, 30, 152, 8, 106, + 134, 175, 34, 252, 25, 129, 202, 224, 87, 8, 62, 134, 144, 98, 236, 41, + 141, 222, 229, 152, 75, 42, 183, + 95, 54, 184, 22, 242, 142, 197, 164, 83, 59, 125, 211, 97, 157, 232, + 105, 142, 174, 228, 124, 75, 97, 247, + 104, 70, 174, 178, 252, 117, 129, 231, 32, 74, 152, 55, 42, 150, 159, + 46, 232, 28, 78, 137, 244, 102, 199, + 106, 210, 175, 29, 188, 9, 177, 198, 244, 82, 199, 125, 146, 161, 173, + 184, 125, 178, 161, 181, 184, 119, 50, + 166, 149, 186, 239, 51, 12, 21, 197, 207, 19, 20, 13, 207, 69, 148, 51, + 47, 85, 220, 63, 25, 208, 10, + 220, 7, 25, 194, 138, 209, 167, 28, 122, 137, 227, 38, 201, 218, 214, + 219, 30, 219, 72, 91, 118, 187, 102, + 243, 106, 197, 239, 19, 12, 13, 197, 197, 147, 19, 45, 205, 221, 149, + 153, 175, 42, 252, 31, 1, 200, 0, + 86, 128, 62, 224, 16, 72, 12, 54, 133, 214, 227, 30, 201, 200, 86, 214, + 190, 222, 240, 88, 68, 58, 179, + 83, 53, 253, 215, 1, 158, 128, 104, 96, 46, 168, 28, 126, 137, 224, + 102, 200, 42, 214, 159, 30, 232, 8, + 78, 134, 180, 98, 247, 105, 134, 174, 226, 252, 73, 129, 246, 224, 70, + 200, 50, 214, 149, 158, 239, 40, 76, + 30, 181, 200, 119, 22, 166, 142, 250, 228, 67, 11, 113, 199, 100, 82, + 171, 125, 191, 97, 176, 40, 116, 30, + 167, 72, 122, 182, 163, 54, 249, 214, 194, 222, 209, 152, 92, 106, 185, + 239, 50, 204, 21, 149, 207, 47, 20, + 28, 15, 73, 196, 54, 211, 86, 221, 254, 217, 128, 90, 224, 59, 8, 19, + 70, 141, 242, 229, 133, 139, 35, + 39, 89, 218, 186, 219, 51, 27, 85, 203, 127, 23, 96, 14, 168, 4, 126, + 131, 96, 97, 232, 40, 78, 158, + 180, 104, 119, 110, 166, 172, 122, 253, 227, 1, 137, 192, 102, 208, 42, + 220, 31, 25, 200, 10, 214, 135, 30, + 226, 136, 73, 166, 182, 250, 246, 195, 6, 209, 194, 220, 81, 153, 252, + 106, 193, 239, 16, 76, 12, 53, 197, + 215, 19, 30, 141, 200, 101, 150, 171, 46, 255, 92, 64, 57, 240, 18, + 196, 13, 147, 69, 173, 243, 61, 133, + 209, 163, 28, 121, 201, 226, 214, 201, 158, 214, 232, 94, 206, 184, 84, + 114, 191, 101, 176, 43, 52, 31, 87, + 72, 62, 182, 144, 118, 236, 38, 205, 218, 213, 155, 31, 43, 72, 31, + 118, 136, 38, 230, 154, 202, 235, 23, + 15, 78, 132, 52, 99, 87, 105, 254, 174, 192, 124, 80, 33, 252, 24, 65, + 202, 176, 87, 52, 62, 151, 80, + 110, 188, 44, 113, 221, 228, 89, 139, 122, 231, 99, 10, 169, 199, 62, + 210, 144, 93, 172, 57, 189, 210, 241, + 157, 132, 105, 163, 110, 249, 236, 66, 205, 241, 149, 132, 111, 35, + 108, 25, 237, 202, 205, 151, 21, 174, 143, + 60, 100, 17, 235, 76, 79, 117, 244, 39, 7, 90, 130, 187, 33, 179, 88, + 117, 250, 167, 3, 58, 129, 211, + 32, 93, 216, 57, 154, 146, 235, 45, 143, 93, 164, 57, 187, 82, 243, + 125, 133, 225, 163, 8, 121, 198, 162, + 210, 249, 157, 130, 233, 161, 142, 248, 100, 66, 171, 113, 191, 100, + 112, 43, 100, 31, 107, 72, 47, 118, 156, + 38, 233, 218, 206, 219, 20, 91, 79, 123, 116, 35, 103, 89, 234, 186, + 207, 51, 20, 21, 207, 79, 20, 52, + 15, 87, 68, 62, 179, 80, 117, 252, 39, 1, 218, 128, 91, 32, 59, 88, 19, + 122, 141, 227, 37, 137, 219, + 38, 219, 90, 219, 123, 27, 99, 75, 105, 247, 110, 198, 172, 82, 253, + 253, 129, 129, 160, 96, 120, 40, 34, + 158, 153, 168, 106, 254, 175, 0, 124, 0, 33, 192, 24, 80, 10, 188, 7, + 49, 194, 148, 81, 175, 124, 124, + 33, 225, 216, 72, 90, 182, 187, 54, 243, 86, 197, 254, 211, 0, 93, 192, + 57, 144, 18, 236, 13, 141, 197, + 165, 147, 59, 45, 211, 93, 157, 249, 169, 130, 254, 225, 128, 72, 96, + 54, 168, 22, 254, 142, 192, 100, 80, + 43, 124, 31, 97, 200, 40, 86, 158, 190, 232, 112, 78, 164, 52, 123, 87, + 99, 126, 169, 224, 126, 200, 32, + 86, 152, 62, 234, 144, 79, 44, 52, 29, 215, 73, 158, 182, 232, 118, + 206, 166, 212, 122, 223, 99, 24, 41, + 202, 158, 215, 40, 94, 158, 184, 104, 114, 174, 165, 188, 123, 49, 227, + 84, 73, 255, 118, 192, 38, 208, 26, + 220, 11, 25, 199, 74, 210, 183, 29, 182, 137, 182, 230, 246, 202, 198, + 215, 18, 222, 141, 152, 101, 170, 171, + 63, 63, 80, 16, 60, 12, 17, 197, 204, 83, 21, 253, 207, 1, 148, 0, 111, + 64, 44, 48, 29, 212, 9, + 159, 70, 232, 50, 206, 149, 148, 111, 47, 108, 28, 45, 201, 221, 150, + 217, 174, 218, 252, 91, 1, 251, 64, + 67, 112, 49, 228, 20, 75, 79, 119, 116, 38, 167, 90, 250, 187, 3, 51, + 65, 213, 240, 95, 4, 56, 3, + 82, 129, 253, 160, 65, 184, 48, 114, 148, 37, 175, 91, 60, 59, 81, 211, + 124, 93, 225, 249, 136, 66, 230, + 177, 138, 244, 103, 7, 106, 130, 175, 33, 188, 24, 113, 202, 164, 87, + 59, 126, 147, 96, 109, 232, 45, 142, + 157, 164, 105, 187, 110, 243, 108, 69, 237, 243, 13, 133, 197, 163, 19, + 57, 205, 210, 213, 157, 159, 41, 168, + 30, 254, 136, 64, 102, 176, 42, 244, 31, 7, 72, 2, 182, 129, 182, 224, + 118, 200, 38, 214, 154, 222, 235, + 24, 79, 74, 180, 55, 55, 86, 150, 190, 238, 240, 76, 68, 53, 243, 87, + 5, 254, 131, 0, 97, 192, 40, + 80, 30, 188, 8, 113, 198, 164, 82, 251, 125, 131, 97, 161, 232, 120, + 78, 162, 180, 121, 183, 98, 246, 169, + 134, 254, 226, 192, 73, 144, 54, 236, 22, 205, 206, 213, 148, 95, 47, + 120, 28, 34, 137, 217, 166, 218, 250, + 219, 3, 27, 65, 203, 112, 87, 100, 62, 171, 80, 127, 124, 32, 33, 216, + 24, 90, 138, 187, 39, 51, 90, + 149, 251, 47, 3, 92, 1, 249, 192, 66, 208, 49, 156, 20, 105, 207, 110, + 212, 44, 95, 93, 248, 57, 130, + 146, 225, 173, 136, 125, 166, 161, 186, 248, 115, 2, 165, 193, 187, 16, + 115, 76, 37, 245, 219, 7, 27, 66, + 139, 113, 167, 100, 122, 171, 99, 63, 105, 208, 46, 220, 28, 89, 201, + 250, 214, 195, 30, 209, 200, 92, 86, + 185, 254, 242, 192, 69, 144, 51, 44, 21, 221, 207, 25, 148, 10, 239, + 71, 12, 50, 133, 213, 163, 31, 57, + 200, 18, 214, 141, 158, 229, 168, 75, 62, 183, 80, 118, 188, 38, 241, + 218, 196, 91, 19, 123, 77, 227, 117, + 137, 231, 38, 202, 154, 215, 43, 30, 159, 72, 104, 54, 174, 150, 252, + 110, 193, 236, 80, 77, 252, 53, 129, + 215, 32, 94, 152, 56, 106, 146, 175, 45, 188, 29, 177, 201, 180, 86, + 247, 126, 198, 160, 82, 248, 61, 130, + 145, 161, 172, 120, 125, 226, 161, 137, 184, 102, 242, 170, 197, 191, + 19, 48, 13, 212, 5, 159, 67, 40, 49, + 222, 148, 88, 111, 122, 172, 35, 61, 217, 209, 154, 220, 107, 25, 239, + 74, 204, 55, 21, 214, 143, 30, 228, + 8, 75, 70, 183, 114, 246, 165, 134, 251, 34, 195, 89, 145, 250, 236, + 67, 13, 241, 197, 132, 83, 35, 125, + 217, 225, 154, 200, 107, 22, 175, 78, 252, 52, 65, 215, 112, 94, 164, + 56, 123, 82, 163, 125, 185, 225, 178, + 200, 117, 150, 167, 46, 250, 156, 67, 41, 241, 222, 196, 88, 83, 122, + 189, 227, 49, 137, 212, 102, 223, 106, + 216, 47, 26, 156, 11, 41, 199, 94, 210, 184, 93, 178, 185, 181, 178, + 247, 53, 134, 151, 34, 238, 153, 140, + 106, 229, 239, 11, 12, 7, 69, 194, 179, 17, 181, 204, 119, 21, 230, + 143, 10, 228, 7, 11, 66, 135, 113, + 162, 164, 121, 187, 98, 243, 105, 133, 238, 227, 12, 73, 197, 246, 211, + 6, 221, 194, 217, 145, 154, 236, 107, + 13, 239, 69, 140, 51, 37, 213, 219, 31, 27, 72, 11, 118, 135, 102, 226, + 170, 201, 191, 22, 240, 14, 196, + 4, 83, 67, 125, 241, 225, 132, 72, 99, 118, 169, 230, 254, 202, 192, + 87, 16, 62, 140, 16, 101, 204, 43, + 21, 223, 79, 24, 52, 10, 151, 71, 46, 178, 156, 117, 169, 231, 62, 202, + 144, 87, 44, 62, 157, 208, 105, + 156, 46, 233, 220, 78, 217, 244, 90, 199, 123, 18, 163, 77, 185, 245, + 178, 199, 53, 146, 151, 45, 174, 157, + 188, 105, 177, 238, 244, 76, 71, 117, 242, 167, 5, 186, 131, 51, 33, + 213, 216, 95, 26, 184, 11, 50, 135, + 85, 162, 191, 57, 176, 18, 244, 13, 135, 69, 162, 179, 57, 181, 210, + 247, 29, 134, 137, 162, 230, 249, 138, + 194, 231, 17, 138, 140, 103, 37, 234, 155, 15, 43, 68, 31, 115, 72, 37, + 246, 155, 6, 235, 66, 207, 113, + 148, 36, 111, 91, 108, 59, 109, 211, 109, 157, 237, 169, 141, 190, 229, + 176, 75, 52, 55, 87, 86, 190, 190, + 240, 112, 68, 36, 51, 91, 85, 251, 127, 3, 96, 1, 232, 0, 78, 128, 52, + 96, 23, 104, 14, 174, 132, + 124, 99, 97, 233, 232, 78, 206, 180, 84, 119, 127, 102, 160, 42, 248, + 31, 2, 136, 1, 166, 128, 122, 224, + 35, 8, 25, 198, 138, 210, 231, 29, 138, 137, 167, 38, 250, 154, 195, + 43, 17, 223, 76, 88, 53, 250, 151, + 3, 46, 129, 220, 96, 89, 232, 58, 206, 147, 20, 109, 207, 109, 148, 45, + 175, 93, 188, 57, 177, 210, 244, + 93, 135, 121, 162, 162, 249, 185, 130, 242, 225, 133, 136, 99, 38, 169, + 218, 254, 219, 0, 91, 64, 59, 112, + 19, 100, 13, 235, 69, 143, 115, 36, 37, 219, 91, 27, 123, 75, 99, 119, + 105, 230, 174, 202, 252, 87, 1, + 254, 128, 64, 96, 48, 40, 20, 30, 143, 72, 100, 54, 171, 86, 255, 126, + 192, 32, 80, 24, 60, 10, 145, + 199, 44, 82, 157, 253, 169, 129, 190, 224, 112, 72, 36, 54, 155, 86, + 235, 126, 207, 96, 84, 40, 63, 94, + 144, 56, 108, 18, 173, 205, 189, 149, 177, 175, 52, 124, 23, 97, 206, + 168, 84, 126, 191, 96, 112, 40, 36, + 30, 155, 72, 107, 118, 175, 102, 252, 42, 193, 223, 16, 88, 12, 58, + 133, 211, 35, 29, 217, 201, 154, 214, + 235, 30, 207, 72, 84, 54, 191, 86, 240, 62, 196, 16, 83, 76, 61, 245, + 209, 135, 28, 98, 137, 233, 166, + 206, 250, 212, 67, 31, 113, 200, 36, 86, 155, 126, 235, 96, 79, 104, + 52, 46, 151, 92, 110, 185, 236, 114, + 205, 229, 149, 139, 47, 39, 92, 26, 185, 203, 50, 215, 85, 158, 191, + 40, 112, 30, 164, 8, 123, 70, 163, + 114, 249, 229, 130, 203, 33, 151, 88, 110, 186, 172, 115, 61, 229, 209, + 139, 28, 103, 73, 234, 182, 207, 54, + 212, 22, 223, 78, 216, 52, 90, 151, 123, 46, 163, 92, 121, 249, 226, + 194, 201, 145, 150, 236, 110, 205, 236, + 85, 141, 255, 37, 128, 27, 32, 11, 88, 7, 122, 130, 163, 33, 185, 216, + 114, 218, 165, 155, 59, 43, 83, + 95, 125, 248, 33, 130, 152, 97, 170, 168, 127, 62, 160, 16, 120, 12, + 34, 133, 217, 163, 26, 249, 203, 2, + 215, 65, 158, 176, 104, 116, 46, 167, 92, 122, 185, 227, 50, 201, 213, + 150, 223, 46, 216, 28, 90, 137, 251, + 38, 195, 90, 209, 251, 28, 67, 73, 241, 246, 196, 70, 211, 114, 221, + 229, 153, +}; + +void scramble(unsigned char *inout) +{ + unsigned char *r = inout + 12; + unsigned char *s = yellowbook_scrambler; + unsigned int i; + + for (i = 2340; i; i--) { + *r++ ^= *s++; + } +} + +/* Calculate the P parities for the sector. + * The 43 P vectors of length 24 are combined with the GF8_P_COEFFS. + */ +void parity_p(unsigned char *sector) +{ + int i, j; + unsigned char p0_msb, p1_msb; + unsigned char p0_lsb, p1_lsb; + unsigned char *p_msb_start, *p_lsb_start; + unsigned char *p_msb, *p_lsb; + unsigned char *coeffs0, *coeffs1; + unsigned char *p0, *p1; + unsigned char d; + unsigned short c; + + p_lsb_start = sector + LEC_HEADER_OFFSET; + p_msb_start = sector + LEC_HEADER_OFFSET + 1; + + p1 = sector + LEC_MODE1_P_PARITY_OFFSET; + p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43; + + for (i = 0; i <= 42; i++) { + p_lsb = p_lsb_start; + p_msb = p_msb_start; + + coeffs0 = gf8_p_coeffs[0]; + coeffs1 = gf8_p_coeffs[1]; + + p0_lsb = p1_lsb = p0_msb = p1_msb = 0; + + for (j = 0; j <= 23; j++) { + d = *p_lsb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + p0_lsb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + p1_lsb ^= gf8_ilog[c]; + } + + d = *p_msb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + p0_msb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + p1_msb ^= gf8_ilog[c]; + } + + coeffs0++; + coeffs1++; + + p_lsb += 2 * 43; + p_msb += 2 * 43; + } + + *p0 = p0_lsb; + *(p0 + 1) = p0_msb; + + *p1 = p1_lsb; + *(p1 + 1) = p1_msb; + + p0 += 2; + p1 += 2; + + p_lsb_start += 2; + p_msb_start += 2; + } +} + +/* Calculate the Q parities for the sector. + * The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS. + */ +void parity_q(unsigned char *sector) +{ + int i, j; + unsigned char q0_msb, q1_msb; + unsigned char q0_lsb, q1_lsb; + unsigned char *q_msb_start, *q_lsb_start; + unsigned char *q_msb, *q_lsb; + unsigned char *coeffs0, *coeffs1; + unsigned char *q0, *q1, *q_start; + unsigned char d; + unsigned short c; + + q_lsb_start = sector + LEC_HEADER_OFFSET; + q_msb_start = sector + LEC_HEADER_OFFSET + 1; + + q_start = sector + LEC_MODE1_Q_PARITY_OFFSET; + q1 = sector + LEC_MODE1_Q_PARITY_OFFSET; + q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26; + + for (i = 0; i <= 25; i++) { + q_lsb = q_lsb_start; + q_msb = q_msb_start; + + coeffs0 = gf8_q_coeffs[0]; + coeffs1 = gf8_q_coeffs[1]; + + q0_lsb = q1_lsb = q0_msb = q1_msb = 0; + + for (j = 0; j <= 42; j++) { + d = *q_lsb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + q0_lsb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + q1_lsb ^= gf8_ilog[c]; + } + + d = *q_msb; + + if (d != 0) { + c = gf8_log[d] + *coeffs0; + if (c >= 255) + c -= 255; + q0_msb ^= gf8_ilog[c]; + + c = gf8_log[d] + *coeffs1; + if (c >= 255) + c -= 255; + q1_msb ^= gf8_ilog[c]; + } + + coeffs0++; + coeffs1++; + + q_lsb += 2 * 44; + q_msb += 2 * 44; + + if (q_lsb >= q_start) { + q_msb -= 2 * 1118; + q_lsb -= 2 * 1118; + } + } + + *q0 = q0_lsb; + *(q0 + 1) = q0_msb; + + *q1 = q1_lsb; + *(q1 + 1) = q1_msb; + + q0 += 2; + q1 += 2; + + q_lsb_start += 2 * 43; + q_msb_start += 2 * 43; + } +} diff --git a/libburn/lec.h b/libburn/lec.h new file mode 100644 index 0000000..f698030 --- /dev/null +++ b/libburn/lec.h @@ -0,0 +1,12 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __LEC +#define __LEC + +#define RS_L12_BITS 8 + +void scramble(unsigned char *); +void parity_p(unsigned char *in); +void parity_q(unsigned char *in); + +#endif /* __LEC */ diff --git a/libburn/libburn.h b/libburn/libburn.h new file mode 100644 index 0000000..7506ed3 --- /dev/null +++ b/libburn/libburn.h @@ -0,0 +1,928 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef LIBBURN_H +#define LIBBURN_H + +/* Needed for off_t which is the (POSIX-ly) appropriate type for + expressing a file or stream size. + + XXX we should enforce 64-bitness for off_t +*/ +#include + +#ifndef DOXYGEN + +#if defined(__cplusplus) +#define BURN_BEGIN_DECLS \ + namespace burn { \ + extern "C" { +#define BURN_END_DECLS \ + } \ + } +#else +#define BURN_BEGIN_DECLS +#define BURN_END_DECLS +#endif + +BURN_BEGIN_DECLS + +#endif + +/** References a physical drive in the system */ +struct burn_drive; + +/** References a whole disc */ +struct burn_disc; + +/** References a single session on a disc */ +struct burn_session; + +/** References a single track on a disc */ +struct burn_track; + +/** Session format for normal audio or data discs */ +#define BURN_CDROM 0 +/** Session format for obsolete CD-I discs */ +#define BURN_CDI 0x10 +/** Session format for CDROM-XA discs */ +#define BURN_CDXA 0x20 + +#define BURN_POS_END 100 + +/** Mask for mode bits */ +#define BURN_MODE_BITS 127 + +/** Track mode - mode 0 data + 0 bytes of user data. it's all 0s. mode 0. get it? HAH +*/ +#define BURN_MODE0 (1 << 0) +/** Track mode - mode "raw" - all 2352 bytes supplied by app + FOR DATA TRACKS ONLY! +*/ +#define BURN_MODE_RAW (1 << 1) +/** Track mode - mode 1 data + 2048 bytes user data, and all the LEC money can buy +*/ +#define BURN_MODE1 (1 << 2) +/** Track mode - mode 2 data + defaults to formless, 2336 bytes of user data, unprotected + | with a data form if required. +*/ +#define BURN_MODE2 (1 << 3) +/** Track mode modifier - Form 1, | with MODE2 for reasonable results + 2048 bytes of user data, 4 bytes of subheader +*/ +#define BURN_FORM1 (1 << 4) +/** Track mode modifier - Form 2, | with MODE2 for reasonable results + lots of user data. not much LEC. +*/ +#define BURN_FORM2 (1 << 5) +/** Track mode - audio + 2352 bytes per sector. may be | with 4ch or preemphasis. + NOT TO BE CONFUSED WITH BURN_MODE_RAW +*/ +#define BURN_AUDIO (1 << 6) +/** Track mode modifier - 4 channel audio. */ +#define BURN_4CH (1 << 7) +/** Track mode modifier - Digital copy permitted, can be set on any track.*/ +#define BURN_COPY (1 << 8) +/** Track mode modifier - 50/15uS pre-emphasis */ +#define BURN_PREEMPHASIS (1 << 9) +/** Input mode modifier - subcodes present packed 16 */ +#define BURN_SUBCODE_P16 (1 << 10) +/** Input mode modifier - subcodes present packed 96 */ +#define BURN_SUBCODE_P96 (1 << 11) +/** Input mode modifier - subcodes present raw 96 */ +#define BURN_SUBCODE_R96 (1 << 12) + +/** Possible disc writing style/modes */ +enum burn_write_types +{ + /** Packet writing. + currently unsupported + */ + BURN_WRITE_PACKET, + /** Track At Once recording. + 2s gaps between tracks, no fonky lead-ins + */ + BURN_WRITE_TAO, + /** Session At Once. + block type MUST be BURN_BLOCK_SAO + */ + BURN_WRITE_SAO, + /** Raw disc at once recording. + all subcodes must be provided by lib or user + only raw block types are supported + */ + BURN_WRITE_RAW +}; + +/** Data format to send to the drive */ +enum burn_block_types +{ + /** sync, headers, edc/ecc provided by lib/user */ + BURN_BLOCK_RAW0 = 1, + /** sync, headers, edc/ecc and p/q subs provided by lib/user */ + BURN_BLOCK_RAW16 = 2, + /** sync, headers, edc/ecc and packed p-w subs provided by lib/user */ + BURN_BLOCK_RAW96P = 4, + /** sync, headers, edc/ecc and raw p-w subs provided by lib/user */ + BURN_BLOCK_RAW96R = 8, + /** only 2048 bytes of user data provided by lib/user */ + BURN_BLOCK_MODE1 = 256, + /** 2336 bytes of user data provided by lib/user */ + BURN_BLOCK_MODE2R = 512, + /** 2048 bytes of user data provided by lib/user + subheader provided in write parameters + are we ever going to support this shit? I vote no. + (supposed to be supported on all drives...) + */ + BURN_BLOCK_MODE2_PATHETIC = 1024, + /** 2048 bytes of data + 8 byte subheader provided by lib/user + hey, this is also dumb + */ + BURN_BLOCK_MODE2_LAME = 2048, + /** 2324 bytes of data provided by lib/user + subheader provided in write parameters + no sir, I don't like it. + */ + BURN_BLOCK_MODE2_OBSCURE = 4096, + /** 2332 bytes of data supplied by lib/user + 8 bytes sub header provided in write parameters + this is the second least suck mode2, and is mandatory for + all drives to support. + */ + BURN_BLOCK_MODE2_OK = 8192, + /** SAO block sizes are based on cue sheet, so use this. */ + BURN_BLOCK_SAO = 16384 +}; + +/** Possible status' of the drive in regard to the disc in it. */ +enum burn_disc_status +{ + /** The current status is not yet known */ + BURN_DISC_UNREADY, + /** The drive holds a blank disc */ + BURN_DISC_BLANK, + /** There is no disc at all in the drive */ + BURN_DISC_EMPTY, + /** There is an incomplete disc in the drive */ + BURN_DISC_APPENDABLE, + /** There is a disc with data on it in the drive */ + BURN_DISC_FULL +}; + +/** Possible types of messages form the library. */ +enum burn_message_type +{ + /** Diagnostic/Process information. For the curious user. */ + BURN_MESSAGE_INFO, + /** A warning regarding a possible problem. The user should probably + be notified, but its not fatal. */ + BURN_MESSAGE_WARNING, + /** An error message. This usually means the current process will be + aborted, and the user should definately see these. */ + BURN_MESSAGE_ERROR +}; + +/** Possible information messages */ +enum burn_message_info +{ + BURN_INFO_FOO +}; + +/** Possible warning messages */ +enum burn_message_warning +{ + BURN_WARNING_FOO +}; + +/** Possible error messages */ +enum burn_message_error +{ + BURN_ERROR_CANCELLED +}; + +/** Possible data source return values */ +enum burn_source_status +{ + /** The source is ok */ + BURN_SOURCE_OK, + /** The source is at end of file */ + BURN_SOURCE_EOF, + /** The source is unusable */ + BURN_SOURCE_FAILED +}; + + +/** Possible busy states for a drive */ +enum burn_drive_status +{ + /** The drive is not in an operation */ + BURN_DRIVE_IDLE, + /** The library is spawning the processes to handle a pending + operation (A read/write/etc is about to start but hasn't quite + yet) */ + BURN_DRIVE_SPAWNING, + /** The drive is reading data from a disc */ + BURN_DRIVE_READING, + /** The drive is writing data to a disc */ + BURN_DRIVE_WRITING, + /** The drive is writing Lead-In */ + BURN_DRIVE_WRITING_LEADIN, + /** The drive is writing Lead-Out */ + BURN_DRIVE_WRITING_LEADOUT, + /** The drive is erasing a disc */ + BURN_DRIVE_ERASING, + /** The drive is being grabbed */ + BURN_DRIVE_GRABBING +}; + +/** Information about a track on a disc - this is from the q sub channel of the + lead-in area of a disc. The documentation here is very terse. + See a document such as mmc3 for proper information. +*/ +struct burn_toc_entry +{ + /** Session the track is in */ + unsigned char session; + /** Type of data. for this struct to be valid, it must be 1 */ + unsigned char adr; + /** Type of data in the track */ + unsigned char control; + /** Zero. Always. Really. */ + unsigned char tno; + /** Track number or special information */ + unsigned char point; + unsigned char min; + unsigned char sec; + unsigned char frame; + unsigned char zero; + /** Track start time minutes for normal tracks */ + unsigned char pmin; + /** Track start time seconds for normal tracks */ + unsigned char psec; + /** Track start time frames for normal tracks */ + unsigned char pframe; +}; + + +/** Data source for tracks */ +struct burn_source { + /** Reference count for the data source. Should be 1 when a new source + is created. Increment it to take a reference for yourself. Use + burn_source_free to destroy your reference to it. */ + int refcount; + + /** Read data from the source */ + int (*read)(struct burn_source *, + unsigned char *buffer, + int size); + + /** Read subchannel data from the source (NULL if lib generated) */ + int (*read_sub)(struct burn_source *, + unsigned char *buffer, + int size); + + /** Get the size of the source's data */ + off_t (*get_size)(struct burn_source *); + + /** Clean up the source specific data */ + void (*free_data)(struct burn_source *); + + /** Next source, for when a source runs dry and padding is disabled + THIS IS AUTOMATICALLY HANDLED, DO NOT TOUCH + */ + struct burn_source *next; + + /** Source specific data */ + void *data; +}; + + +/** Information on a drive in the system */ +struct burn_drive_info +{ + /** Name of the vendor of the drive */ + char vendor[9]; + /** Name of the drive */ + char product[17]; + /** Revision of the drive */ + char revision[5]; + /** Location of the drive in the filesystem. */ + char location[17]; + + /** Can the drive read DVD-RAM discs */ + unsigned int read_dvdram:1; + /** Can the drive read DVD-R discs */ + unsigned int read_dvdr:1; + /** Can the drive read DVD-ROM discs */ + unsigned int read_dvdrom:1; + /** Can the drive read CD-R discs */ + unsigned int read_cdr:1; + /** Can the drive read CD-RW discs */ + unsigned int read_cdrw:1; + + /** Can the drive write DVD-RAM discs */ + unsigned int write_dvdram:1; + /** Can the drive write DVD-R discs */ + unsigned int write_dvdr:1; + /** Can the drive write CD-R discs */ + unsigned int write_cdr:1; + /** Can the drive write CD-RW discs */ + unsigned int write_cdrw:1; + + /** Can the drive simulate a write */ + unsigned int write_simulate:1; + + /** Can the drive report C2 errors */ + unsigned int c2_errors:1; + + /** The size of the drive's buffer (in kilobytes) */ + int buffer_size; + /** + * The supported block types in tao mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int tao_block_types; + /** + * The supported block types in sao mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int sao_block_types; + /** + * The supported block types in raw mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int raw_block_types; + /** + * The supported block types in packet mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int packet_block_types; + + /** The value by which this drive can be indexed when using functions + in the library. This is the value to pass to all libbburn functions + that operate on a drive. */ + struct burn_drive *drive; +}; + +/** Messages from the library */ +struct burn_message +{ + /** The drive associated with the message. NULL if the error is not + related to a specific drive. */ + struct burn_drive *drive; + + /** The type of message this is. See message_type for details. */ + enum burn_message_type type; + + /** The actual message */ + union detail { + struct { + enum burn_message_info message; + } info; + struct { + enum burn_message_warning message; + } warning; + struct { + enum burn_message_error message; + } error; + } detail; +}; + +/** Operation progress report. All values are 0 based indices. + * */ +struct burn_progress { + /** The total number of sessions */ + int sessions; + /** Current session.*/ + int session; + /** The total number of tracks */ + int tracks; + /** Current track. */ + int track; + /** The total number of indices */ + int indices; + /** Curent index. */ + int index; + /** The starting logical block address */ + int start_sector; + /** The number of sector */ + int sectors; + /** The current sector being processed */ + int sector; +}; + +/** Initialize the library. + This must be called before using any other functions in the library. It + may be called more than once with no effect. + If is possible to 'restart' the library by shutting it down and + re-initializing it, though there is no good reason to do that. + @return Nonzero if the library was able to initialize; zero if + initialization failed. +*/ +int burn_initialize(void); + +/** Shutdown the library. + This should be called before exiting your application. Make sure that all + drives you have grabbed are released before calling this. +*/ +void burn_finish(void); + +/** Set the verbosity level of the library. The default value is 0, which means + that nothing is output on stderr. The more you increase this, the more + debug output should be displayed on stderr for you. + @param level The verbosity level desired. 0 for nothing, higher positive + values for more information output. +*/ +void burn_set_verbosity(int level); + +/** Returns a newly allocated burn_message structure. This message should be + freed with burn_message_free() when you are finished with it. + @return A message or NULL when there are no more messages to retrieve. +*/ +struct burn_message* burn_get_message(void); + +/** Frees a burn_message structure */ +void burn_message_free(struct burn_message *msg); + +/** Scans for drives. This function MUST be called until it returns nonzero. + No drives can be in use when this is called or it will assert. + All drive pointers are invalidated by using this function. Do NOT store + drive pointers across calls to this function or death AND pain will ensue. + When the app is done with the burn_drive_info array, it must be freed with + burn_drive_info_free() + @param drives Returns an array of drives (cdroms/burners). The returned + array should be freed when it is no longer needed, and + before calling this function again to rescan. + @param n_drives Returns the number of hardware drives in @c drives. + @return Zero while scanning is not complete; non-zero when it is finished. +*/ +int burn_drive_scan(struct burn_drive_info *drives[], + unsigned int *n_drives); +/** Frees a burn_drive_info array returned by burn_drive_scan + @param info The array to free +*/ +void burn_drive_info_free(struct burn_drive_info *info); + +/** Grab a drive. This must be done before the drive can be used (for reading, + writing, etc). It may be neccesary to call this function more than once + to grab a drive. See burn_grab for details. + @param drive The drive to grab. This is found in a returned + burn_drive_info struct. + @param load Nonzero to make the drive attempt to load a disc (close its + tray door, etc). + @return 1 if the drive has been grabbed, else 0 +*/ +int burn_drive_grab(struct burn_drive *drive, int load); + +/** Release a drive. This should not be done until the drive is no longer + busy (see burn_drive_get_status). + @param drive The drive to release. + @param eject Nonzero to make the drive eject the disc in it. +*/ +void burn_drive_release(struct burn_drive *drive, int eject); + +/** Returns what kind of disc a drive is holding. This function may need to be + called more than once to get a proper status from it. See burn_status + for details. + @param drive The drive to query for a disc. + @return The status of the drive, or what kind of disc is in it. +*/ +enum burn_disc_status burn_disc_get_status(struct burn_drive *drive); + +/** Tells whether a disc can be erased or not + @return Non-zero means erasable +*/ +int burn_disc_erasable(struct burn_drive *d); + +/** Returns the progress and status of a drive. + @param drive The drive to query busy state for. + @param p Returns the progress of the operation, NULL if you don't care + @return the current status of the drive. See also burn_drive_status. +*/ +enum burn_drive_status burn_drive_get_status(struct burn_drive *drive, + struct burn_progress *p); + +/** Creates a write_opts struct for burning to the specified drive + must be freed with burn_write_opts_free + @param drive The drive to write with + @return The write_opts +*/ +struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive); + +/** Frees a write_opts struct created with burn_write_opts_new + @param opts write_opts to free +*/ +void burn_write_opts_free(struct burn_write_opts *opts); + +/** Creates a write_opts struct for reading from the specified drive + must be freed with burn_write_opts_free + @param drive The drive to read from + @return The read_opts +*/ +struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive); + +/** Frees a read_opts struct created with burn_read_opts_new + @param opts write_opts to free +*/ +void burn_read_opts_free(struct burn_read_opts *opts); + +/** Erase a disc in the drive. The drive must be grabbed successfully BEFORE + calling this functions. Always ensure that the drive reports a status of + BURN_DISC_FULL before calling this function. An erase operation is not + cancellable, as control of the operation is passed wholly to the drive and + there is no way to interrupt it safely. + @param drive The drive with which to erase a disc. + @param fast Nonzero to do a fast erase, where only the disc's headers are + erased; zero to erase the entire disc. +*/ +void burn_disc_erase(struct burn_drive *drive, int fast); + +/** Read a disc from the drive and write it to an fd pair. The drive must be + grabbed successfully BEFORE calling this function. Always ensure that the + drive reports a status of BURN_DISC_FULL before calling this function. + @param drive The drive from which to read a disc. + @param o The options for the read operation. +*/ +void burn_disc_read(struct burn_drive *drive, const struct burn_read_opts *o); + +/** Write a disc in the drive. The drive must be grabbed successfully BEFORE + calling this function. Always ensure that the drive reports a status of + BURN_DISC_BLANK or BURN_STATUS_FULL (to append a new session to the + disc) before calling this function. + @param o The options for the writing operation. + @param disc The struct burn_disc * that described the disc to be created +*/ +void burn_disc_write(struct burn_write_opts *o, struct burn_disc *disc); + +/** Cancel an operation on a drive. + This will only work when the drive's busy state is BURN_DRIVE_READING or + BURN_DRIVE_WRITING. + @param drive The drive on which to cancel the current operation. +*/ +void burn_drive_cancel(struct burn_drive *drive); + +/** Convert a minute-second-frame (MSF) value to sector count + @param m Minute component + @param s Second component + @param f Frame component + @return The sector count +*/ +int burn_msf_to_sectors(int m, int s, int f); + +/** Convert a sector count to minute-second-frame (MSF) + @param sectors The sector count + @param m Returns the minute component + @param s Returns the second component + @param f Returns the frame component +*/ +void burn_sectors_to_msf(int sectors, int *m, int *s, int *f); + +/** Convert a minute-second-frame (MSF) value to an lba + @param m Minute component + @param s Second component + @param f Frame component + @return The lba +*/ +int burn_msf_to_lba(int m, int s, int f); + +/** Convert an lba to minute-second-frame (MSF) + @param lba The lba + @param m Returns the minute component + @param s Returns the second component + @param f Returns the frame component +*/ +void burn_lba_to_msf(int lba, int *m, int *s, int *f); + +/** Create a new disc (for DAO recording)*/ +struct burn_disc *burn_disc_create(void); + +/** Delete disc and decrease the reference count on all its sessions + @param d The disc to be freed +*/ +void burn_disc_free(struct burn_disc *d); + +/** Create a new session (For SAO at once recording, or to be added to a + disc for DAO) +*/ +struct burn_session *burn_session_create(void); + +/** Free a session (and decrease reference count on all tracks inside) + @param s Session to be freed +*/ +void burn_session_free(struct burn_session *s); + +/** Add a session to a disc at a specific position, increasing the + sessions's reference count. + @param d Disc to add the session to + @param s Session to add to the disc + @param pos position to add at (BURN_POS_END is "at the end") + @return 0 for failure, 1 for success +*/ +int burn_disc_add_session(struct burn_disc *d, struct burn_session *s, + unsigned int pos); + +/** Remove a session from a disc + @param d Disc to remove session from + @param s Session pointer to find and remove +*/ +int burn_disc_remove_session(struct burn_disc *d, struct burn_session *s); + + +/** Create a track (for TAO recording, or to put in a session) */ +struct burn_track *burn_track_create(void); + +/** Free a track + @param t Track to free +*/ +void burn_track_free(struct burn_track *t); + +/** Add a track to a session at specified position + @param s Session to add to + @param t Track to insert in session + @param pos position to add at (BURN_POS_END is "at the end") + @return 0 for failure, 1 for success +*/ +int burn_session_add_track(struct burn_session *s, struct burn_track *t, + unsigned int pos); + +/** Remove a track from a session + @param s Session to remove track from + @param t Track pointer to find and remove + @return 0 for failure, 1 for success +*/ +int burn_session_remove_track(struct burn_session *s, struct burn_track *t); + + +/** Define the data in a track + @param t the track to define + @param offset The lib will write this many 0s before start of data + @param tail The number of extra 0s to write after data + @param pad 1 means the lib should pad the last sector with 0s if the + track isn't exactly sector sized. (otherwise the lib will + begin reading from the next track) + @param mode data format (bitfield) +*/ +void burn_track_define_data(struct burn_track *t, int offset, int tail, + int pad, int mode); + +/** Set the ISRC details for a track + @param t The track to change + @param country the 2 char country code. Each character must be + only numbers or letters. + @param owner 3 char owner code. Each character must be only numbers + or letters. + @param year 2 digit year. A number in 0-99 (Yep, not Y2K friendly). + @param serial 5 digit serial number. A number in 0-99999. +*/ +void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, + unsigned char year, unsigned int serial); + +/** Disable ISRC parameters for a track + @param t The track to change +*/ +void burn_track_clear_isrc(struct burn_track *t); + +/** Hide the first track in the "pre gap" of the disc + @param s session to change + @param onoff 1 to enable hiding, 0 to disable +*/ +void burn_session_hide_first_track(struct burn_session *s, int onoff); + +/** Get the drive's disc struct - free when done + @param d drive to query + @return the disc struct +*/ +struct burn_disc *burn_drive_get_disc(struct burn_drive *d); + +/** Set the track's data source + @param t The track to set the data source for + @param s The data source to use for the contents of the track + @return An error code stating if the source is ready for use for + writing the track, or if an error occured + +*/ +enum burn_source_status burn_track_set_source(struct burn_track *t, + struct burn_source *s); + +/** Free a burn_source (decrease its refcount and maybe free it) + @param s Source to free +*/ +void burn_source_free(struct burn_source *s); + +/** Creates a data source for an image file (and maybe subcode file) */ +struct burn_source *burn_file_source_new(const char *path, + const char *subpath); + +/** Creates a data source for an image file (resp. a track) from an open + readable filedescriptor, an eventually open readable subcodes file + descriptor and eventually a fixed size in bytes. + @param datafd The source of data. + @param subfd The eventual source for subcodes. Not used if -1. + @param size The eventual fixed size of eventually both fds. + If this value is 0, the size will be determined from datafd. +*/ +struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size); + +/** Tells how long a track will be on disc */ +int burn_track_get_sectors(struct burn_track *); + + +/** Sets drive read and write speed + @param d The drive to set speed for + @param read Read speed in k/s (0 is max) + @param write Write speed in k/s (0 is max) +*/ +void burn_drive_set_speed(struct burn_drive *d, int read, int write); + +/* these are for my debugging, they will disappear */ +void burn_structure_print_disc(struct burn_disc *d); +void burn_structure_print_session(struct burn_session *s); +void burn_structure_print_track(struct burn_track *t); + +/** Sets the write type for the write_opts struct + @param opts The write opts to change + @param write_type The write type to use + @param block_type The block type to use + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_write_type(struct burn_write_opts *opts, + enum burn_write_types write_type, + int block_type); + +/** Supplies toc entries for writing - not normally required for cd mastering + @param opts The write opts to change + @param count The number of entries + @param toc_entries +*/ +void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, + int count, + struct burn_toc_entry *toc_entries); + +/** Sets the session format for a disc + @param opts The write opts to change + @param format The session format to set +*/ +void burn_write_opts_set_format(struct burn_write_opts *opts, int format); + +/** Sets the simulate value for the write_opts struct + @param opts The write opts to change + @param sim If non-zero, the drive will perform a simulation instead of a burn + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim); + +/** Controls buffer underrun prevention + @param opts The write opts to change + @param underrun_proof if non-zero, buffer underrun protection is enabled + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, + int underrun_proof); + +/** Sets whether to use opc or not with the write_opts struct + @param opts The write opts to change + @param opc If non-zero, optical power calibration will be performed at + start of burn + +*/ +void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc); + +void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, int has_mediacatalog); + +void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned char mediacatalog[13]); + +/** Sets whether to read in raw mode or not + @param opts The read opts to change + @param raw_mode If non-zero, reading will be done in raw mode, so that everything in the data tracks on the + disc is read, including headers. +*/ +void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw_mode); + +/** Sets whether to report c2 errors or not + @param opts The read opts to change + @param c2errors If non-zero, report c2 errors. +*/ +void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors); + +/** Sets whether to read subcodes from audio tracks or not + @param opts The read opts to change + @param subcodes_audio If non-zero, read subcodes from audio tracks on the disc. +*/ +void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts, + int subcodes_audio); + +/** Sets whether to read subcodes from data tracks or not + @param opts The read opts to change + @param subcodes_data If non-zero, read subcodes from data tracks on the disc. +*/ +void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts, + int subcodes_data); + +/** Sets whether to recover errors if possible + @param opts The read opts to change + @param hardware_error_recovery If non-zero, attempt to recover errors if possible. +*/ +void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts, + int hardware_error_recovery); + +/** Sets whether to report recovered errors or not + @param opts The read opts to change + @param report_recovered_errors If non-zero, recovered errors will be reported. +*/ +void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts, + int report_recovered_errors); + +/** Sets whether blocks with unrecoverable errors should be read or not + @param opts The read opts to change + @param transfer_damaged_blocks If non-zero, blocks with unrecoverable errors will still be read. +*/ +void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts, + int transfer_damaged_blocks); + +/** Sets the number of retries to attempt when trying to correct an error + @param opts The read opts to change + @param hardware_error_retries The number of retries to attempt when correcting an error. +*/ +void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts, + unsigned char hardware_error_retries); + +/** Gets the maximum write speed for a drive + @param d Drive to query + @return Maximum write speed in K/s +*/ +int burn_drive_get_write_speed(struct burn_drive *d); + +/** Gets the maximum read speed for a drive + @param d Drive to query + @return Maximum read speed in K/s +*/ +int burn_drive_get_read_speed(struct burn_drive *d); + +/** Gets a copy of the toc_entry structure associated with a track + @param t Track to get the entry from + @param entry Struct for the library to fill out +*/ +void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry); + +/** Gets a copy of the toc_entry structure associated with a session's lead out + @param s Session to get the entry from + @param entry Struct for the library to fill out +*/ +void burn_session_get_leadout_entry(struct burn_session *s, + struct burn_toc_entry *entry); + +/** Gets an array of all the sessions for the disc + THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A SESSION + @param d Disc to get session array for + @param num Returns the number of sessions in the array + @return array of sessions +*/ +struct burn_session **burn_disc_get_sessions(struct burn_disc *d, + int *num); + +int burn_disc_get_sectors(struct burn_disc *d); + +/** Gets an array of all the tracks for a session + THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A TRACK + @param s session to get track array for + @param num Returns the number of tracks in the array + @return array of tracks +*/ +struct burn_track **burn_session_get_tracks(struct burn_session *s, + int *num); + +int burn_session_get_sectors(struct burn_session *s); + +/** Gets the mode of a track + @param track the track to query + @return the track's mode +*/ +int burn_track_get_mode(struct burn_track *track); + +/** Returns whether the first track of a session is hidden in the pregap + @param session the session to query + @return non-zero means the first track is hidden +*/ +int burn_session_get_hidefirst(struct burn_session *session); + +/** Returns the library's version in its parts + @param major The major version number + @param minor The minor version number + @param micro The micro version number +*/ +void burn_version(int *major, int *minor, int *micro); + +#ifndef DOXYGEN + +BURN_END_DECLS + +#endif + +#endif /*LIBBURN_H*/ diff --git a/libburn/message.c b/libburn/message.c new file mode 100644 index 0000000..71f35bc --- /dev/null +++ b/libburn/message.c @@ -0,0 +1,108 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "message.h" +#include "libburn.h" +#include "debug.h" + +#include + +struct message_list +{ + struct message_list *next; + + struct burn_message *msg; +}; + +static struct message_list *queue; + +void burn_message_free(struct burn_message *msg) +{ + free(msg); +} + +void burn_message_clear_queue(void) +{ + struct burn_message *msg; + + if ((msg = burn_get_message())) { + burn_print(0, + "YOU HAVE MESSAGES QUEUED FROM THE LAST OPERATION. " + "YOU SHOULD BE GRABBING THEM ALL!\n"); + do { + burn_message_free(msg); + } while ((msg = burn_get_message())); + } +} + +struct burn_message *burn_get_message() +{ + struct burn_message *msg = NULL; + + if (queue) { + struct message_list *next; + + next = queue->next; + msg = queue->msg; + free(queue); + queue = next; + } + + return msg; +} + +static void queue_push_tail(struct burn_message *msg) +{ + struct message_list *node; + + node = malloc(sizeof(struct message_list)); + node->next = NULL; + node->msg = msg; + + if (!queue) + queue = node; + else { + struct message_list *it; + + for (it = queue; it->next; it = it->next) ; + it->next = node; + } +} + +void burn_message_info_new(struct burn_drive *drive, + enum burn_message_info message) +{ + struct burn_message *msg; + + msg = malloc(sizeof(struct burn_message)); + msg->drive = drive; + msg->type = BURN_MESSAGE_INFO; + msg->detail.info.message = message; + + queue_push_tail(msg); +} + +void burn_message_warning_new(struct burn_drive *drive, + enum burn_message_info message) +{ + struct burn_message *msg; + + msg = malloc(sizeof(struct burn_message)); + msg->drive = drive; + msg->type = BURN_MESSAGE_WARNING; + msg->detail.warning.message = message; + + queue_push_tail(msg); +} + +void burn_message_error_new(struct burn_drive *drive, + enum burn_message_info message) +{ + struct burn_message *msg; + + msg = malloc(sizeof(struct burn_message)); + msg->drive = drive; + msg->type = BURN_MESSAGE_ERROR; + msg->detail.error.message = message; + + queue_push_tail(msg); +} diff --git a/libburn/message.h b/libburn/message.h new file mode 100644 index 0000000..32613cd --- /dev/null +++ b/libburn/message.h @@ -0,0 +1,19 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __MESSAGE +#define __MESSAGE + +#include "libburn.h" + +void burn_message_clear_queue(void); + +void burn_message_info_new(struct burn_drive *drive, + enum burn_message_info message); + +void burn_message_warning_new(struct burn_drive *drive, + enum burn_message_info message); + +void burn_message_error_new(struct burn_drive *drive, + enum burn_message_info message); + +#endif diff --git a/libburn/mmc.c b/libburn/mmc.c new file mode 100644 index 0000000..efc0b66 --- /dev/null +++ b/libburn/mmc.c @@ -0,0 +1,506 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "sector.h" +#include "libburn.h" +#include "transport.h" +#include "mmc.h" +#include "spc.h" +#include "drive.h" +#include "debug.h" +#include "toc.h" +#include "structure.h" +#include "options.h" + +static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_GET_ATIP[] = { 0x43, 2, 4, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_GET_DISC_INFO[] = + { 0x51, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_READ_CD[] = { 0xBE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_ERASE[] = { 0xA1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_SEND_OPC[] = { 0x54, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_SET_SPEED[] = + { 0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_WRITE_12[] = + { 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_WRITE_10[] = { 0x2A, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_GET_CONFIGURATION[] = + { 0x46, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_SYNC_CACHE[] = { 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_GET_EVENT[] = { 0x4A, 1, 0, 0, 16, 0, 0, 0, 8, 0 }; +static unsigned char MMC_CLOSE[] = { 0x5B, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char MMC_TRACK_INFO[] = { 0x52, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char MMC_SEND_CUE_SHEET[] = + { 0x5D, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +void mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s) +{ + struct buffer buf; + struct command c; + + c.retry = 1; + c.oplen = sizeof(MMC_SEND_CUE_SHEET); + memcpy(c.opcode, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET)); + c.page = &buf; + c.page->bytes = s->count * 8; + c.page->sectors = 0; + c.opcode[6] = (c.page->bytes >> 16) & 0xFF; + c.opcode[7] = (c.page->bytes >> 8) & 0xFF; + c.opcode[8] = c.page->bytes & 0xFF; + c.dir = TO_DRIVE; + memcpy(c.page->data, s->data, c.page->bytes); + d->issue_command(d, &c); +} + +int mmc_get_nwa(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + unsigned char *data; + + c.retry = 1; + c.oplen = sizeof(MMC_TRACK_INFO); + memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO)); + c.opcode[1] = 1; + c.opcode[5] = 0xFF; + c.page = &buf; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + data = c.page->data; + return (data[12] << 24) + (data[13] << 16) + + (data[14] << 8) + data[15]; +} + +void mmc_close_disc(struct burn_drive *d, struct burn_write_opts *o) +{ + assert(o->drive == d); + o->multi = 0; + spc_select_write_params(d, o); + mmc_close(d, 1, 0); +} + +void mmc_close_session(struct burn_drive *d, struct burn_write_opts *o) +{ + assert(o->drive == d); + o->multi = 3; + spc_select_write_params(d, o); + mmc_close(d, 1, 0); +} + +void mmc_close(struct burn_drive *d, int session, int track) +{ + struct command c; + + c.retry = 1; + c.oplen = sizeof(MMC_CLOSE); + memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE)); + c.opcode[2] = session | !!track; + c.opcode[4] = track >> 8; + c.opcode[5] = track & 0xFF; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_get_event(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + + c.retry = 1; + c.oplen = sizeof(MMC_GET_EVENT); + memcpy(c.opcode, MMC_GET_EVENT, sizeof(MMC_GET_EVENT)); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + burn_print(12, "0x%x:0x%x:0x%x:0x%x\n", + c.page->data[0], c.page->data[1], c.page->data[2], + c.page->data[3]); + burn_print(12, "event: %d:%d:%d:%d\n", c.page->data[4], + c.page->data[5], c.page->data[6], c.page->data[7]); +} + +void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf) +{ + struct command c; + int len; + + len = buf->sectors; + assert(buf->bytes >= buf->sectors); /* can be == at 0... */ + burn_print(100, "trying to write %d at %d\n", len, start); + memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12)); + c.retry = 1; + c.oplen = sizeof(MMC_WRITE_12); + c.opcode[2] = start >> 24; + c.opcode[3] = (start >> 16) & 0xFF; + c.opcode[4] = (start >> 8) & 0xFF; + c.opcode[5] = start & 0xFF; + c.opcode[6] = len >> 24; + c.opcode[7] = (len >> 16) & 0xFF; + c.opcode[8] = (len >> 8) & 0xFF; + c.opcode[9] = len & 0xFF; + c.page = buf; + c.dir = TO_DRIVE; + + d->issue_command(d, &c); +} + +int mmc_write(struct burn_drive *d, int start, struct buffer *buf) +{ + int cancelled; + struct command c; + int len; + + pthread_mutex_lock(&d->access_lock); + cancelled = d->cancel; + pthread_mutex_unlock(&d->access_lock); + + if (cancelled) + return BE_CANCELLED; + + len = buf->sectors; + assert(buf->bytes >= buf->sectors); /* can be == at 0... */ + burn_print(100, "trying to write %d at %d\n", len, start); + memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10)); + c.retry = 1; + c.oplen = sizeof(MMC_WRITE_10); + c.opcode[2] = start >> 24; + c.opcode[3] = (start >> 16) & 0xFF; + c.opcode[4] = (start >> 8) & 0xFF; + c.opcode[5] = start & 0xFF; + c.opcode[6] = 0; + c.opcode[7] = (len >> 8) & 0xFF; + c.opcode[8] = len & 0xFF; + c.page = buf; + c.dir = TO_DRIVE; +/* + burn_print(12, "%d, %d, %d, %d - ", c->opcode[2], c->opcode[3], c->opcode[4], c->opcode[5]); + burn_print(12, "%d, %d, %d, %d\n", c->opcode[6], c->opcode[7], c->opcode[8], c->opcode[9]); +*/ + +/* write(fileno(stderr), c.page->data, c.page->bytes);*/ + + d->issue_command(d, &c); + return 0; +} + +void mmc_read_toc(struct burn_drive *d) +{ +/* read full toc, all sessions, in m/s/f form, 4k buffer */ + struct burn_track *track; + struct burn_session *session; + struct buffer buf; + struct command c; + int dlen; + int i; + unsigned char *tdata; + + memcpy(c.opcode, MMC_GET_TOC, sizeof(MMC_GET_TOC)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_TOC); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { + d->busy = BURN_DRIVE_IDLE; + return; + } + + dlen = c.page->data[0] * 256 + c.page->data[1]; + d->toc_entries = (dlen - 2) / 11; +/* + some drives fail this check. + + assert(((dlen - 2) % 11) == 0); +*/ + d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry)); + tdata = c.page->data + 4; + + burn_print(12, "TOC:\n"); + + d->disc = burn_disc_create(); + + for (i = 0; i < c.page->data[3]; i++) { + session = burn_session_create(); + burn_disc_add_session(d->disc, session, BURN_POS_END); + burn_session_free(session); + } + for (i = 0; i < d->toc_entries; i++, tdata += 11) { + burn_print(12, "S %d, PT %d, TNO %d : ", tdata[0], tdata[3], + tdata[2]); + burn_print(12, "(%d:%d:%d)", tdata[8], tdata[9], tdata[10]); + burn_print(12, "A(%d:%d:%d)", tdata[4], tdata[5], tdata[6]); + burn_print(12, " - control %d, adr %d\n", tdata[1] & 0xF, + tdata[1] >> 4); + + if (tdata[3] == 1) { + if (burn_msf_to_lba(tdata[8], tdata[9], tdata[10])) { + d->disc->session[0]->hidefirst = 1; + track = burn_track_create(); + burn_session_add_track(d->disc-> + session[tdata[0] - 1], + track, BURN_POS_END); + burn_track_free(track); + + } + } + if (tdata[3] < 100) { + track = burn_track_create(); + burn_session_add_track(d->disc->session[tdata[0] - 1], + track, BURN_POS_END); + track->entry = &d->toc_entry[i]; + burn_track_free(track); + } + d->toc_entry[i].session = tdata[0]; + d->toc_entry[i].adr = tdata[1] >> 4; + d->toc_entry[i].control = tdata[1] & 0xF; + d->toc_entry[i].tno = tdata[2]; + d->toc_entry[i].point = tdata[3]; + d->toc_entry[i].min = tdata[4]; + d->toc_entry[i].sec = tdata[5]; + d->toc_entry[i].frame = tdata[6]; + d->toc_entry[i].zero = tdata[7]; + d->toc_entry[i].pmin = tdata[8]; + d->toc_entry[i].psec = tdata[9]; + d->toc_entry[i].pframe = tdata[10]; + if (tdata[3] == 0xA0) + d->disc->session[tdata[0] - 1]->firsttrack = tdata[8]; + if (tdata[3] == 0xA1) + d->disc->session[tdata[0] - 1]->lasttrack = tdata[8]; + if (tdata[3] == 0xA2) + d->disc->session[tdata[0] - 1]->leadout_entry = + &d->toc_entry[i]; + } + if (d->status != BURN_DISC_APPENDABLE) + d->status = BURN_DISC_FULL; + toc_find_modes(d); +} + +void mmc_read_disc_info(struct burn_drive *d) +{ + struct buffer buf; + unsigned char *data; + struct command c; + + memcpy(c.opcode, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_DISC_INFO); + c.page = &buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { + d->busy = BURN_DRIVE_IDLE; + return; + } + + data = c.page->data; + d->erasable = !!(data[2] & 16); + switch (data[2] & 3) { + case 0: + d->toc_entries = 0; + d->start_lba = burn_msf_to_lba(data[17], data[18], data[19]); + d->end_lba = burn_msf_to_lba(data[21], data[22], data[23]); + d->status = BURN_DISC_BLANK; + break; + case 1: + d->status = BURN_DISC_APPENDABLE; + case 2: + mmc_read_toc(d); + break; + } +} + +void mmc_read_atip(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + + memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_ATIP); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + burn_print(1, "atip shit for you\n"); +} + +void mmc_read_sectors(struct burn_drive *d, + int start, + int len, + const struct burn_read_opts *o, struct buffer *buf) +{ + int temp; + int errorblock, req; + struct command c; + + assert(len >= 0); +/* if the drive isn't busy, why the hell are we here? */ + assert(d->busy); + burn_print(12, "reading %d from %d\n", len, start); + memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD)); + c.retry = 1; + c.oplen = sizeof(MMC_READ_CD); + temp = start; + c.opcode[5] = temp & 0xFF; + temp >>= 8; + c.opcode[4] = temp & 0xFF; + temp >>= 8; + c.opcode[3] = temp & 0xFF; + temp >>= 8; + c.opcode[2] = temp & 0xFF; + c.opcode[8] = len & 0xFF; + len >>= 8; + c.opcode[7] = len & 0xFF; + len >>= 8; + c.opcode[6] = len & 0xFF; + req = 0xF8; + if (d->busy == BURN_DRIVE_GRABBING || o->report_recovered_errors) + req |= 2; + + c.opcode[10] = 0; +/* always read the subcode, throw it away later, since we don't know + what we're really reading +*/ + if (d->busy == BURN_DRIVE_GRABBING || (o->subcodes_audio) + || (o->subcodes_data)) + c.opcode[10] = 1; + + c.opcode[9] = req; + c.page = buf; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + if (c.error) { + burn_print(12, "got an error over here\n"); + burn_print(12, "%d, %d, %d, %d\n", c.sense[3], c.sense[4], + c.sense[5], c.sense[6]); + errorblock = + (c.sense[3] << 24) + (c.sense[4] << 16) + + (c.sense[5] << 8) + c.sense[6]; + c.page->sectors = errorblock - start + 1; + burn_print(1, "error on block %d\n", errorblock); + burn_print(12, "error on block %d\n", errorblock); + burn_print(12, "returning %d sectors\n", c.page->sectors); + } +} + +void mmc_erase(struct burn_drive *d, int fast) +{ + struct command c; + + memcpy(c.opcode, MMC_ERASE, sizeof(MMC_ERASE)); + c.opcode[1] = 16; /* IMMED set to 1 */ + c.opcode[1] |= !!fast; + c.retry = 1; + c.oplen = sizeof(MMC_ERASE); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_read_lead_in(struct burn_drive *d, struct buffer *buf) +{ + int len; + struct command c; + + len = buf->sectors; + memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD)); + c.retry = 1; + c.oplen = sizeof(MMC_READ_CD); + c.opcode[5] = 0; + c.opcode[4] = 0; + c.opcode[3] = 0; + c.opcode[2] = 0xF0; + c.opcode[8] = 1; + c.opcode[7] = 0; + c.opcode[6] = 0; + c.opcode[9] = 0; + c.opcode[10] = 2; + c.page = buf; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); +} + +void mmc_perform_opc(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, MMC_SEND_OPC, sizeof(MMC_SEND_OPC)); + c.retry = 1; + c.oplen = sizeof(MMC_SEND_OPC); + c.opcode[1] = 1; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_set_speed(struct burn_drive *d, int r, int w) +{ + struct command c; + + memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED)); + c.retry = 1; + c.oplen = sizeof(MMC_SET_SPEED); + c.opcode[2] = r >> 8; + c.opcode[3] = r & 0xFF; + c.opcode[4] = w >> 8; + c.opcode[5] = w & 0xFF; + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void mmc_get_configuration(struct burn_drive *d) +{ + struct buffer buf; + int len; + struct command c; + + memcpy(c.opcode, MMC_GET_CONFIGURATION, sizeof(MMC_GET_CONFIGURATION)); + c.retry = 1; + c.oplen = sizeof(MMC_GET_CONFIGURATION); + c.page = &buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + burn_print(1, "got it back\n"); + len = (c.page->data[0] << 24) + + (c.page->data[1] << 16) + + (c.page->data[2] << 8) + + c.page->data[3]; + burn_print(1, "all %d bytes of it\n", len); + burn_print(1, "%d, %d, %d, %d\n", + c.page->data[0], + c.page->data[1], c.page->data[2], c.page->data[3]); +} + +void mmc_sync_cache(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE)); + c.retry = 1; + c.oplen = sizeof(MMC_SYNC_CACHE); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} diff --git a/libburn/mmc.h b/libburn/mmc.h new file mode 100644 index 0000000..95275b7 --- /dev/null +++ b/libburn/mmc.h @@ -0,0 +1,37 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __MMC +#define __MMC + +struct burn_drive; +struct burn_write_opts; +struct command; +struct buffer; +struct cue_sheet; + +/* MMC commands */ + +void mmc_read(struct burn_drive *); +void mmc_close_session(struct burn_drive *, struct burn_write_opts *); +void mmc_close_disc(struct burn_drive *, struct burn_write_opts *); +void mmc_close(struct burn_drive *, int session, int track); +void mmc_get_event(struct burn_drive *); +int mmc_write(struct burn_drive *, int start, struct buffer *buf); +void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf); +void mmc_sync_cache(struct burn_drive *); +void mmc_load(struct burn_drive *); +void mmc_eject(struct burn_drive *); +void mmc_erase(struct burn_drive *, int); +void mmc_read_toc(struct burn_drive *); +void mmc_read_disc_info(struct burn_drive *); +void mmc_read_atip(struct burn_drive *); +void mmc_read_sectors(struct burn_drive *, + int, + int, const struct burn_read_opts *, struct buffer *); +void mmc_set_speed(struct burn_drive *, int, int); +void mmc_read_lead_in(struct burn_drive *, struct buffer *); +void mmc_perform_opc(struct burn_drive *); +void mmc_get_configuration(struct burn_drive *); +int mmc_get_nwa(struct burn_drive *); +void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *); +#endif /*__MMC*/ diff --git a/libburn/null.c b/libburn/null.c new file mode 100644 index 0000000..415e0a4 --- /dev/null +++ b/libburn/null.c @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "null.h" +#include "libburn.h" +#include + +#include +int null_read(struct burn_source *source, unsigned char *buffer, int size) +{ + memset(buffer, 0, size); + return size; +} + +struct burn_source *burn_null_source_new(void) +{ + struct burn_source *src; + + src = malloc(sizeof(struct burn_source)); + src->refcount = 1; + src->read = null_read; + src->read_sub = NULL; + + src->get_size = 0; + src->free_data = NULL; + src->data = NULL; + return src; +} diff --git a/libburn/null.h b/libburn/null.h new file mode 100644 index 0000000..1a7aae3 --- /dev/null +++ b/libburn/null.h @@ -0,0 +1,10 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__NULL_H +#define BURN__NULL_H + +struct burn_source; +int null_read(struct burn_source *source, unsigned char *buffer, int size); +struct burn_source *burn_null_source_new(void); + +#endif /* LIBBURN__NULL_H */ diff --git a/libburn/options.c b/libburn/options.c new file mode 100644 index 0000000..938a23c --- /dev/null +++ b/libburn/options.c @@ -0,0 +1,169 @@ +#include "libburn.h" +#include "options.h" +#include "transport.h" + +#include +#include +#include +struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive) +{ + struct burn_write_opts *opts; + + opts = malloc(sizeof(struct burn_write_opts)); + opts->drive = drive; + opts->refcount = 1; + opts->write_type = BURN_WRITE_TAO; + opts->block_type = BURN_BLOCK_MODE1; + opts->toc_entry = NULL; + opts->toc_entries = 0; + opts->simulate = 0; + opts->underrun_proof = drive->mdata->underrun_proof; + opts->perform_opc = 1; + opts->has_mediacatalog = 0; + opts->format = BURN_CDROM; + opts->multi = 0; + opts->control = 0; + return opts; +} + +void burn_write_opts_free(struct burn_write_opts *opts) +{ + if (--opts->refcount <= 0) + free(opts); +} + +struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive) +{ + struct burn_read_opts *opts; + + opts = malloc(sizeof(struct burn_read_opts)); + opts->drive = drive; + opts->refcount = 1; + opts->raw = 0; + opts->c2errors = 0; + opts->subcodes_audio = 0; + opts->subcodes_data = 0; + opts->hardware_error_recovery = 0; + opts->report_recovered_errors = 0; + opts->transfer_damaged_blocks = 0; + opts->hardware_error_retries = 3; + + return opts; +} + +void burn_read_opts_free(struct burn_read_opts *opts) +{ + if (--opts->refcount <= 0) + free(opts); +} + +int burn_write_opts_set_write_type(struct burn_write_opts *opts, + enum burn_write_types write_type, + int block_type) +{ + if ((write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO) || + (opts->drive->block_types[write_type] & block_type)) { + opts->write_type = write_type; + opts->block_type = block_type; + return 1; + } + assert(0); + return 0; +} + +void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count, + struct burn_toc_entry *toc_entries) +{ + opts->toc_entries = count; + opts->toc_entry = malloc(count * sizeof(struct burn_toc_entry)); + memcpy(opts->toc_entry, &toc_entries, + sizeof(struct burn_toc_entry) * count); +} + +void burn_write_opts_set_format(struct burn_write_opts *opts, int format) +{ + opts->format = format; +} + +int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim) +{ + if (opts->drive->mdata->simulate) { + opts->simulate = sim; + return 1; + } + return 0; +} + +int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, + int underrun_proof) +{ + if (opts->drive->mdata->underrun_proof) { + opts->underrun_proof = underrun_proof; + return 1; + } + return 0; +} + +void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc) +{ + opts->perform_opc = opc; +} + +void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, + int has_mediacatalog) +{ + opts->has_mediacatalog = has_mediacatalog; +} + +void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, + unsigned char mediacatalog[13]) +{ + memcpy(opts->mediacatalog, &mediacatalog, 13); +} + +void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw) +{ + opts->raw = raw; +} + +void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors) +{ + opts->c2errors = c2errors; +} + +void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts, + int subcodes_audio) +{ + opts->subcodes_audio = subcodes_audio; +} + +void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts, + int subcodes_data) +{ + opts->subcodes_data = subcodes_data; +} + +void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts, + int hardware_error_recovery) +{ + opts->hardware_error_recovery = hardware_error_recovery; +} + +void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts, + int report_recovered_errors) +{ + opts->report_recovered_errors = report_recovered_errors; +} + +void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts, + int transfer_damaged_blocks) +{ + opts->transfer_damaged_blocks = transfer_damaged_blocks; +} + +void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts, + unsigned char + hardware_error_retries) +{ + opts->hardware_error_retries = hardware_error_retries; +} diff --git a/libburn/options.h b/libburn/options.h new file mode 100644 index 0000000..aebfdb5 --- /dev/null +++ b/libburn/options.h @@ -0,0 +1,78 @@ +#ifndef BURN__OPTIONS_H +#define BURN__OPTIONS_H + +#include "libburn.h" + +/** Options for disc writing operations. This should be created with + burn_write_opts_new() and freed with burn_write_opts_free(). */ +struct burn_write_opts +{ + /** Drive the write opts are good for */ + struct burn_drive *drive; + + /** For internal use. */ + int refcount; + + /** The method/style of writing to use. */ + enum burn_write_types write_type; + /** format of the data to send to the drive */ + enum burn_block_types block_type; + + /** Number of toc entries. if this is 0, they will be auto generated*/ + int toc_entries; + /** Toc entries for the disc */ + struct burn_toc_entry *toc_entry; + + /** Simulate the write so that the disc is not actually written */ + unsigned int simulate:1; + /** If available, enable a drive feature which prevents buffer + underruns if not enough data is available to keep up with the + drive. */ + unsigned int underrun_proof:1; + /** Perform calibration of the drive's laser before beginning the + write. */ + unsigned int perform_opc:1; + /** A disc can have a media catalog number */ + int has_mediacatalog; + unsigned char mediacatalog[13]; + /** Session format */ + int format; + /* internal use only */ + unsigned char control; + unsigned char multi; +}; + +/** Options for disc reading operations. This should be created with + burn_read_opts_new() and freed with burn_read_opts_free(). */ +struct burn_read_opts +{ + /** Drive the read opts are good for */ + struct burn_drive *drive; + + /** For internal use. */ + int refcount; + + /** Read in raw mode, so that everything in the data tracks on the + disc is read, including headers. Not needed if just reading a + filesystem off a disc, but it should usually be used when making a + disc image or copying a disc. */ + unsigned int raw:1; + /** Report c2 errors. Useful for statistics reporting */ + unsigned int c2errors:1; + /** Read subcodes from audio tracks on the disc */ + unsigned int subcodes_audio:1; + /** Read subcodes from data tracks on the disc */ + unsigned int subcodes_data:1; + /** Have the drive recover errors if possible */ + unsigned int hardware_error_recovery:1; + /** Report errors even when they were recovered from */ + unsigned int report_recovered_errors:1; + /** Read blocks even when there are unrecoverable errors in them */ + unsigned int transfer_damaged_blocks:1; + + /** The number of retries the hardware should make to correct + errors. */ + unsigned char hardware_error_retries; +}; + +#endif /* BURN__OPTIONS_H */ diff --git a/libburn/read.c b/libburn/read.c new file mode 100644 index 0000000..4417ef1 --- /dev/null +++ b/libburn/read.c @@ -0,0 +1,264 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include "sector.h" +#include "libburn.h" +#include "drive.h" +#include "transport.h" +#include "message.h" +#include "crc.h" +#include "debug.h" +#include "init.h" +#include "lec.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "read.h" +#include "options.h" + +void burn_disc_read(struct burn_drive *d, const struct burn_read_opts *o) +{ +#if 0 + int i, end, maxsects, finish; + int seclen; + int drive_lba; + unsigned short crc; + unsigned char fakesub[96]; + struct buffer page; + int speed; + + assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000)); + + assert(!d->busy); + assert(d->toc->valid); + assert(o->datafd != -1); +/* XXX not sure this is a good idea. copy it? */ +/* XXX also, we have duplicated data now, do we remove the fds from struct +drive, or only store a subset of the _opts structs in drives */ + + /* set the speed on the drive */ + speed = o->speed > 0 ? o->speed : d->mdata->max_read_speed; + d->set_speed(d, speed, 0); + + d->params.retries = o->hardware_error_retries; + d->send_parameters(d, o); + + d->cancel = 0; + d->busy = BURN_DRIVE_READING; + d->currsession = 0; +/* drive_lba = 232000; + d->currtrack = 18; +*/ + d->currtrack = 0; + drive_lba = 0; +/* XXX removal of this line obviously breaks * + d->track_end = burn_track_end(d, d->currsession, d->currtrack);*/ + printf("track ends at %d\n", d->track_end); + page.sectors = 0; + page.bytes = 0; + + if (o->subfd != -1) { + memset(fakesub, 0xFF, 12); + memset(fakesub + 12, 0, 84); + fakesub[13] = 1; + fakesub[14] = 1; + fakesub[20] = 2; + fakesub[12] = (d->toc->toc_entry[0].control << 4) + + d->toc->toc_entry[0].adr; + crc = crc_ccitt(fakesub + 12, 10); + fakesub[22] = crc >> 8; + fakesub[23] = crc & 0xFF; + write(o->subfd, fakesub, 96); + } + while (1) { + seclen = burn_sector_length_read(d, o); + + burn_print(12, "received %d blocks\n", page.sectors); + for (i = 0; i < page.sectors; i++) { + burn_packet_process(d, page.data + seclen * i, o); + d->track_end--; + drive_lba++; + } + + if ((d->cancel) || (drive_lba == LAST_SESSION_END(d))) { + burn_print(1, "finished or cancelled\n"); + d->busy = BURN_DRIVE_IDLE; + if (!d->cancel) + d->toc->complete = 1; + return; + } +/* XXX: removal of this line obviously breaks * + end = burn_track_end(d, d->currsession, d->currtrack); */ + + if (drive_lba == end) { + d->currtrack++; + if (d->currtrack > + d->toc->session[d->currsession].lasttrack) { + d->currsession++; + burn_print(12, "session switch to %d\n", + d->currsession); + burn_print(12, "skipping a lead out\n"); + drive_lba = CURRENT_SESSION_START(d); + burn_print(12, "new lba %d\n", drive_lba); +/* XXX more of the same + end = burn_track_end(d, d->currsession, + d->currtrack); +*/ } + burn_print(12, "track switch to %d\n", d->currtrack); + } + + page.sectors = 0; + page.bytes = 0; + + maxsects = BUFFER_SIZE / seclen; + finish = end - drive_lba; + + d->track_end = finish; + + page.sectors = (finish < maxsects) ? finish : maxsects; + printf("reading %d sectors from %d\n", page.sectors, + drive_lba); + d->read_sectors(d, drive_lba, page.sectors, o, &page); + printf("Read %d\n", page.sectors); + } +#endif +} +int burn_sector_length_read(struct burn_drive *d, + const struct burn_read_opts *o) +{ + int dlen = 2352; + int data; + +/*XXX how do we handle this crap now?*/ +/* data = d->toc->track[d->currtrack].toc_entry->control & 4;*/ + data = 1; + if (o->report_recovered_errors) + dlen += 294; + if ((o->subcodes_data) && data) + dlen += 96; + if ((o->subcodes_audio) && !data) + dlen += 96; + return dlen; +} + +static int bitcount(unsigned char *data, int n) +{ + int i, j, count = 0; + unsigned char tem; + + for (i = 0; i < n; i++) { + tem = data[i]; + for (j = 0; j < 8; j++) { + count += tem & 1; + tem >>= 1; + } + } + return count; +} + +void burn_packet_process(struct burn_drive *d, unsigned char *data, + const struct burn_read_opts *o) +{ + unsigned char sub[96]; + unsigned short crc; + int ptr = 2352, i, j, code, fb; + int audio = 1; + + if (o->c2errors) { + fb = bitcount(data + ptr, 294); + if (fb) { + burn_print(1, "%d damaged bits\n", + bitcount(data + ptr, 294)); + burn_print(1, "sending error on %s %s\n", + d->idata->vendor, d->idata->product); + /* XXX send a burn_message! burn_message_error(d, + something); */ + } + ptr += 294; + } +/* + if (d->toc->track[d->currtrack].mode == BURN_MODE_UNINITIALIZED) { + if ((d->toc->track[d->currtrack].toc_entry->control & 4) == 0) + d->toc->track[d->currtrack].mode = BURN_MODE_AUDIO; + else + switch (data[15]) { + case 0: + d->toc->track[d->currtrack].mode = BURN_MODE0; + break; + case 1: + d->toc->track[d->currtrack].mode = BURN_MODE1; + break; + case 2: + d->toc->track[d->currtrack].mode = + BURN_MODE2_FORMLESS; + break; + } + } +*/ + if ((audio && o->subcodes_audio) + || (!audio && o->subcodes_data)) { + memset(sub, 0, sizeof(sub)); + for (i = 0; i < 12; i++) { + for (j = 0; j < 8; j++) { + for (code = 0; code < 8; code++) { + sub[code * 12 + i] <<= 1; + if (data[ptr + j + i * 8] & + (1 << (7 - code))) + sub[code * 12 + i]++; + } + } + } + crc = (*(sub + 22) << 8) + *(sub + 23); + if (crc != crc_ccitt(sub + 12, 10)) { + burn_print(1, "sending error on %s %s\n", + d->idata->vendor, d->idata->product); +/* e = burn_error(); + e->drive = d; +*/ + burn_print(1, "crc mismatch in Q\n"); + } + /* else process_q(d, sub + 12); */ + /* + if (o->subfd != -1) write(o->subfd, sub, 96); */ + } +/* + if ((d->track_end <= 150) + && (drive_lba + 150 < CURRENT_SESSION_END(d)) + && (TOC_ENTRY(d->toc, d->currtrack).control == 4) + && (TOC_ENTRY(d->toc, d->currtrack + 1).control == 0)) { + burn_print(12, "pregap : %d\n", d->track_end); + write(o->binfd, zeros, 2352); + +#warning XXX WHERE ARE MY SUBCODES + } else +*//* write(o->datafd, data, 2352); */ +} + +/* so yeah, when you uncomment these, make them write zeros insted of crap +static void write_empty_sector(int fd) +{ + char sec[2352]; + + burn_print(1, "writing an 'empty' sector\n"); + write(fd, sec, 2352); +} + +static void write_empty_subcode(int fd) +{ + char sub[96]; + + write(fd, sub, 96); +} + +static void flipq(unsigned char *sub) +{ + *(sub + 12 + 10) = ~*(sub + 12 + 10); + *(sub + 12 + 11) = ~*(sub + 12 + 11); +} +*/ diff --git a/libburn/read.h b/libburn/read.h new file mode 100644 index 0000000..fc07958 --- /dev/null +++ b/libburn/read.h @@ -0,0 +1,14 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __LIBBURN_READ +#define __LIBBURN_READ + +struct burn_drive; +struct burn_read_opts; + +int burn_sector_length_read(struct burn_drive *d, + const struct burn_read_opts *o); +void burn_packet_process(struct burn_drive *d, unsigned char *data, + const struct burn_read_opts *o); + +#endif /* __LIBBURN_READ */ diff --git a/libburn/sbc.c b/libburn/sbc.c new file mode 100644 index 0000000..dcd64ae --- /dev/null +++ b/libburn/sbc.c @@ -0,0 +1,40 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* scsi block commands */ + +#include +#include +#include + +#include "transport.h" +#include "sbc.h" +#include "options.h" + +/* spc command set */ +static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 }; +static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 }; + +void sbc_load(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SBC_LOAD, sizeof(SBC_LOAD)); + c.retry = 1; + c.oplen = sizeof(SBC_LOAD); + c.dir = NO_TRANSFER; + c.page = NULL; + d->issue_command(d, &c); +} + +void sbc_eject(struct burn_drive *d) +{ + struct command c; + + c.page = NULL; + memcpy(c.opcode, SBC_UNLOAD, sizeof(SBC_UNLOAD)); + c.oplen = 1; + c.oplen = sizeof(SBC_UNLOAD); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} diff --git a/libburn/sbc.h b/libburn/sbc.h new file mode 100644 index 0000000..3ca028c --- /dev/null +++ b/libburn/sbc.h @@ -0,0 +1,11 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SBC +#define __SBC + +struct burn_drive; + +void sbc_load(struct burn_drive *); +void sbc_eject(struct burn_drive *); + +#endif /* __SBC */ diff --git a/libburn/sector.c b/libburn/sector.c new file mode 100644 index 0000000..2d7ce17 --- /dev/null +++ b/libburn/sector.c @@ -0,0 +1,649 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include "error.h" +#include "options.h" +#include "transport.h" +#include "libburn.h" +#include "drive.h" +#include "sector.h" +#include "crc.h" +#include "debug.h" +#include "lec.h" +#include "toc.h" +#include "write.h" + +/*static unsigned char isrc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";*/ + +#define sector_common(X) d->alba++; d->rlba X; + +static void uncook_subs(unsigned char *dest, unsigned char *source) +{ + int i, j, code; + + memset(dest, 0, 96); + + for (i = 0; i < 12; i++) { + for (j = 0; j < 8; j++) { + for (code = 0; code < 8; code++) { + if (source[code * 12 + i] & 0x80) + dest[j + i * 8] |= (1 << (7 - code)); + source[code * 12 + i] <<= 1; + } + } + } +} + +/* 0 means "same as inmode" */ +static int get_outmode(struct burn_write_opts *o) +{ + if (o->write_type == BURN_WRITE_SAO) + return 0; + else + switch (o->block_type) { + case BURN_BLOCK_RAW0: + return BURN_MODE_RAW; + case BURN_BLOCK_RAW16: + return BURN_MODE_RAW | BURN_SUBCODE_P16; + case BURN_BLOCK_RAW96P: + return BURN_MODE_RAW | BURN_SUBCODE_P96; + case BURN_BLOCK_RAW96R: + return BURN_MODE_RAW | BURN_SUBCODE_R96; + case BURN_BLOCK_MODE1: + return BURN_MODE1; + } + assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */ +} + +static void get_bytes(struct burn_track *track, int count, unsigned char *data) +{ + int valid, shortage, curr; + +/* no track pointer means we're just generating 0s */ + if (!track) { + memset(data, 0, count); + return; + } + +/* first we use up any offset */ + valid = track->offset - track->offsetcount; + if (valid > count) + valid = count; + + if (valid) { + track->offsetcount += valid; + memset(data, 0, valid); + } + shortage = count - valid; + + if (!shortage) + return; + +/* Next we use source data */ + curr = valid; + if (!track->eos) { + valid = track->source->read(track->source, data + curr, count - curr); + } else valid = 0; + + if (valid == -1) { + track->eos = 1; + valid = 0; + } + + curr += valid; + shortage = count - curr; + + if (!shortage) + return; + +/* Before going to the next track, we run through any tail */ + + valid = track->tail - track->tailcount; + if (valid > count - curr) + valid = count - curr; + + if (valid) { + track->tailcount += valid; + memset(data + curr, 0, valid); + } + curr += valid; + shortage -= valid; + + if (!shortage) + return; + +/* If we're still short, and there's a "next" pointer, we pull from that. + if that depletes, we'll just fill with 0s. +*/ + if (track->source->next) { + struct burn_source *src; + printf("pulling from next track\n"); + src = track->source->next; + valid = src->read(src, data + curr, shortage); + if (valid > 0) { + shortage -= valid; + curr += valid; + } + } + if (!shortage) + return; + memset(data + curr, 0, shortage); +} +static unsigned char *get_sector(struct burn_write_opts *opts, int inmode) +{ + struct burn_drive *d = opts->drive; + struct buffer *out = d->buffer; + int outmode; + int seclen; + unsigned char *ret; + + outmode = get_outmode(opts); + if (outmode == 0) + outmode = inmode; + + seclen = burn_sector_length(outmode) + burn_subcode_length(outmode); + + if (out->bytes + (seclen) >= BUFFER_SIZE) { + int err; + err = d->write(d, d->nwa, out); + if (err == BE_CANCELLED) + return NULL; + d->nwa += out->sectors; + out->bytes = 0; + out->sectors = 0; + } + + ret = out->data + out->bytes; + out->bytes += seclen; + out->sectors++; + + return ret; +} + +/* either inmode == outmode, or outmode == raw. anything else is bad news */ +static void convert_data(struct burn_write_opts *o, struct burn_track *track, + int inmode, unsigned char *data) +{ + int outlen, inlen; + int offset = -1; + int outmode; + + outmode = get_outmode(o); + if (outmode == 0) + outmode = inmode; + + outlen = burn_sector_length(outmode); + inlen = burn_sector_length(inmode); + assert(outlen >= inlen); + + if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) { + get_bytes(track, inlen, data); + return; + } + + assert(outmode & BURN_MODE_RAW); + + if (inmode & BURN_MODE1) + offset = 16; + if (inmode & BURN_MODE_RAW) + offset = 0; + if (inmode & BURN_AUDIO) + offset = 0; + assert(offset != -1); + get_bytes(track, inlen, data + offset); +} +static void convert_subs(struct burn_write_opts *o, int inmode, + unsigned char *subs, unsigned char *sector) +{ + unsigned char *out; + int outmode; + + outmode = get_outmode(o); + if (outmode == 0) + outmode = inmode; + sector += burn_sector_length(outmode); +/* XXX for sao with subs, we'd need something else... */ + + switch (o->block_type) { + case BURN_BLOCK_RAW96R: + uncook_subs(sector, subs); + break; + + case BURN_BLOCK_RAW16: + memcpy(sector, subs + 12, 12); + out = sector + 12; + out[0] = 0; + out[1] = 0; + out[2] = 0; +/*XXX find a better way to deal with partially damaged P channels*/ + if (subs[2] != 0) + out[3] = 0x80; + else + out[3] = 0; + out = sector + 10; + + out[0] = ~out[0]; + out[1] = ~out[1]; + break; + } +} + +static void subcode_toc(struct burn_drive *d, int mode, unsigned char *data) +{ + unsigned char *q; + int track; + int crc; + int min, sec, frame; + + track = d->toc_temp / 3; + memset(data, 0, 96); + q = data + 12; + + burn_lba_to_msf(d->rlba, &min, &sec, &frame); +/*XXX track numbers are BCD +a0 - 1st track ctrl +a1 - last track ctrl +a2 - lout ctrl +*/ + q[0] = (d->toc_entry[track].control << 4) + 1; + q[1] = 0; + if (d->toc_entry[track].point < 100) + q[2] = dec_to_bcd(d->toc_entry[track].point); + else + q[2] = d->toc_entry[track].point; + q[3] = dec_to_bcd(min); + q[4] = dec_to_bcd(sec); + q[5] = dec_to_bcd(frame); + q[6] = 0; + q[7] = dec_to_bcd(d->toc_entry[track].pmin); + q[8] = dec_to_bcd(d->toc_entry[track].psec); + q[9] = dec_to_bcd(d->toc_entry[track].pframe); + crc = crc_ccitt(q, 10); + q[10] = crc >> 8; + q[11] = crc & 0xFF; + d->toc_temp++; + d->toc_temp %= (d->toc_entries * 3); +} + +int sector_toc(struct burn_write_opts *o, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char *data; + unsigned char subs[96]; + + data = get_sector(o, mode); + if (!data) + return 0; + convert_data(o, NULL, mode, data); + subcode_toc(d, mode, subs); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 1); + sector_common(++) + return 1; +} + +int sector_pregap(struct burn_write_opts *o, + unsigned char tno, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char *data; + unsigned char subs[96]; + + data = get_sector(o, mode); + if (!data) + return 0; + convert_data(o, NULL, mode, data); + subcode_user(o, subs, tno, control, 0, NULL, 1); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 0); + sector_common(--) + return 1; +} + +int sector_postgap(struct burn_write_opts *o, + unsigned char tno, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char subs[96]; + unsigned char *data; + + data = get_sector(o, mode); + if (!data) + return 0; + convert_data(o, NULL, mode, data); +/* use last index in track */ + subcode_user(o, subs, tno, control, 1, NULL, 1); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 0); + sector_common(++) + return 1; +} + +static void subcode_lout(struct burn_write_opts *o, unsigned char control, + unsigned char *data) +{ + struct burn_drive *d = o->drive; + unsigned char *q; + int crc; + int rmin, min, rsec, sec, rframe, frame; + + memset(data, 0, 96); + q = data + 12; + + burn_lba_to_msf(d->alba, &min, &sec, &frame); + burn_lba_to_msf(d->rlba, &rmin, &rsec, &rframe); + + if (((rmin == 0) && (rsec == 0) && (rframe == 0)) || + ((rsec >= 2) && !((rframe / 19) % 2))) + memset(data, 0xFF, 12); + q[0] = (control << 4) + 1; + q[1] = 0xAA; + q[2] = 0x01; + q[3] = dec_to_bcd(rmin); + q[4] = dec_to_bcd(rsec); + q[5] = dec_to_bcd(rframe); + q[6] = 0; + q[7] = dec_to_bcd(min); + q[8] = dec_to_bcd(sec); + q[9] = dec_to_bcd(frame); + crc = crc_ccitt(q, 10); + q[10] = crc >> 8; + q[11] = crc & 0xFF; +} + +static char char_to_isrc(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'Z') + return 0x11 + (c - 'A'); + if (c >= 'a' && c <= 'z') + return 0x11 + (c - 'a'); + assert(0); + return 0; +} + +void subcode_user(struct burn_write_opts *o, unsigned char *subcodes, + unsigned char tno, unsigned char control, + unsigned char indx, struct isrc *isrc, int psub) +{ + struct burn_drive *d = o->drive; + unsigned char *p, *q; + int crc; + int m, s, f, c, qmode; /* 1, 2 or 3 */ + + memset(subcodes, 0, 96); + + p = subcodes; + if ((tno == 1) && (d->rlba == -150)) + memset(p, 0xFF, 12); + + if (psub) + memset(p, 0xFF, 12); + q = subcodes + 12; + + qmode = 1; + /* every 1 in 10 we can do something different */ + if (d->rlba % 10 == 0) { + /* each of these can occur 1 in 100 */ + if ((d->rlba / 10) % 10 == 0) { + if (o->has_mediacatalog) + qmode = 2; + } else if ((d->rlba / 10) % 10 == 1) { + if (isrc && isrc->has_isrc) + qmode = 3; + } + } + + assert(qmode == 1 || qmode == 2 || qmode == 3); + + switch (qmode) { + case 1: + q[1] = dec_to_bcd(tno); /* track number */ + q[2] = dec_to_bcd(indx); /* index XXX read this shit + from the track array */ + burn_lba_to_msf(d->rlba, &m, &s, &f); + q[3] = dec_to_bcd(m); /* rel min */ + q[4] = dec_to_bcd(s); /* rel sec */ + q[5] = dec_to_bcd(f); /* rel frame */ + q[6] = 0; /* zero */ + burn_lba_to_msf(d->alba, &m, &s, &f); + q[7] = dec_to_bcd(m); /* abs min */ + q[8] = dec_to_bcd(s); /* abs sec */ + q[9] = dec_to_bcd(f); /* abs frame */ + break; + case 2: + /* media catalog number */ + q[1] = (o->mediacatalog[0] << 4) + o->mediacatalog[1]; + q[2] = (o->mediacatalog[2] << 4) + o->mediacatalog[3]; + q[3] = (o->mediacatalog[4] << 4) + o->mediacatalog[5]; + q[4] = (o->mediacatalog[6] << 4) + o->mediacatalog[7]; + q[5] = (o->mediacatalog[8] << 4) + o->mediacatalog[9]; + q[6] = (o->mediacatalog[10] << 4) + o->mediacatalog[11]; + q[7] = o->mediacatalog[12] << 4; + + q[8] = 0; + burn_lba_to_msf(d->alba, &m, &s, &f); + q[9] = dec_to_bcd(f); /* abs frame */ + break; + case 3: + c = char_to_isrc(isrc->country[0]); + /* top 6 bits of [1] is the first country code */ + q[1] = c << 2; + c = char_to_isrc(isrc->country[1]); + /* bottom 2 bits of [1] is part of the second country code */ + q[1] += (c >> 4); + /* top 4 bits if [2] is the rest of the second country code */ + q[2] = c << 4; + + c = char_to_isrc(isrc->owner[0]); + /* bottom 4 bits of [2] is part of the first owner code */ + q[2] += (c >> 2); + /* top 2 bits of [3] is the rest of the first owner code */ + q[3] = c << 6; + c = char_to_isrc(isrc->owner[1]); + /* bottom 6 bits of [3] is the entire second owner code */ + q[3] += c; + c = char_to_isrc(isrc->owner[2]); + /* top 6 bits of [4] are the third owner code */ + q[4] = c << 2; + + /* [5] is the year in 2 BCD numbers */ + q[5] = dec_to_bcd(isrc->year % 100); + /* [6] is the first 2 digits in the serial */ + q[6] = dec_to_bcd(isrc->serial % 100); + /* [7] is the next 2 digits in the serial */ + q[7] = dec_to_bcd((isrc->serial / 100) % 100); + /* the top 4 bits of [8] is the last serial digit, the rest is + zeros */ + q[8] = dec_to_bcd((isrc->serial / 10000) % 10) << 4; + burn_lba_to_msf(d->alba, &m, &s, &f); + q[9] = dec_to_bcd(f); /* abs frame */ + break; + } + q[0] = (control << 4) + qmode; + + crc = crc_ccitt(q, 10); + q[10] = crc >> 8; + q[11] = crc & 0xff; +} + +int sector_lout(struct burn_write_opts *o, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + unsigned char subs[96]; + unsigned char *data; + + data = get_sector(o, mode); + if (!data) + return 0; + convert_data(o, NULL, mode, data); + subcode_lout(o, control, subs); + convert_subs(o, mode, subs, data); + sector_headers(o, data, mode, 0); + sector_common(++) + return 1; +} + +int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub) +{ + struct burn_drive *d = o->drive; + unsigned char subs[96]; + unsigned char *data; + + data = get_sector(o, t->mode); + if (!data) + return 0; + convert_data(o, t, t->mode, data); + + if (!t->source->read_sub) + subcode_user(o, subs, t->entry->point, + t->entry->control, 1, &t->isrc, psub); + else if (!t->source->read_sub(t->source, subs, 96)) + subcode_user(o, subs, t->entry->point, + t->entry->control, 1, &t->isrc, psub); + convert_subs(o, t->mode, subs, data); + + sector_headers(o, data, t->mode, 0); + sector_common(++) + return 1; +} + +int burn_msf_to_lba(int m, int s, int f) +{ + if (m < 90) + return (m * 60 + s) * 75 + f - 150; + else + return (m * 60 + s) * 75 + f - 450150; +} + +void burn_lba_to_msf(int lba, int *m, int *s, int *f) +{ + if (lba >= -150) { + *m = (lba + 150) / (60 * 75); + *s = (lba + 150 - *m * 60 * 75) / 75; + *f = lba + 150 - *m * 60 * 75 - *s * 75; + } else { + *m = (lba + 450150) / (60 * 75); + *s = (lba + 450150 - *m * 60 * 75) / 75; + *f = lba + 450150 - *m * 60 * 75 - *s * 75; + } +} + +int dec_to_bcd(int d) +{ + int top, bottom; + + top = d / 10; + bottom = d - (top * 10); + return (top << 4) + bottom; +} + +void sector_headers(struct burn_write_opts *o, unsigned char *out, + int mode, int leadin) +{ + struct burn_drive *d = o->drive; + unsigned int crc; + int min, sec, frame; + int modebyte = -1; + + if (mode & BURN_AUDIO) /* no headers for "audio" */ + return; + if (o->write_type == BURN_WRITE_SAO) + return; + if (mode & BURN_MODE1) + modebyte = 1; + + assert(modebyte == 1); + + out[0] = 0; + memset(out + 1, 0xFF, 10); /* sync */ + out[11] = 0; + + if (leadin) { + burn_lba_to_msf(d->rlba, &min, &sec, &frame); + out[12] = dec_to_bcd(min) + 0xA0; + out[13] = dec_to_bcd(sec); + out[14] = dec_to_bcd(frame); + out[15] = modebyte; + } else { + burn_lba_to_msf(d->alba, &min, &sec, &frame); + out[12] = dec_to_bcd(min); + out[13] = dec_to_bcd(sec); + out[14] = dec_to_bcd(frame); + out[15] = modebyte; + } + if (mode & BURN_MODE1) { + crc = crc_32(out, 2064); + out[2064] = crc & 0xFF; + crc >>= 8; + out[2065] = crc & 0xFF; + crc >>= 8; + out[2066] = crc & 0xFF; + crc >>= 8; + out[2067] = crc & 0xFF; + } + if (mode & BURN_MODE1) { + memset(out + 2068, 0, 8); + parity_p(out); + parity_q(out); + } + scramble(out); +} + +#if 0 +void process_q(struct burn_drive *d, unsigned char *q) +{ + unsigned char i[5]; + int mode; + + mode = q[0] & 0xF; +/* burn_print(12, "mode: %d : ", mode);*/ + switch (mode) { + case 1: +/* burn_print(12, "tno = %d : ", q[1]); + burn_print(12, "index = %d\n", q[2]); +*/ + /* q[1] is the track number (starting at 1) q[2] is the index + number (starting at 0) */ +#warning this is totally bogus + if (q[1] - 1 > 99) + break; + if (q[2] > d->toc->track[q[1] - 1].indices) { + burn_print(12, "new index at %d\n", d->alba); + d->toc->track[q[1] - 1].index[q[2]] = d->alba; + d->toc->track[q[1] - 1].indices++; + } + break; + case 2: + /* XXX dont ignore these */ + break; + case 3: +/* burn_print(12, "ISRC data in mode 3 q\n");*/ + i[0] = isrc[(q[1] << 2) >> 2]; +/* burn_print(12, "0x%x 0x%x 0x%x 0x%x 0x%x\n", q[1], q[2], q[3], q[4], q[5]); + burn_print(12, "ISRC - %c%c%c%c%c\n", i[0], i[1], i[2], i[3], i[4]); +*/ + break; + default: + assert(0); + } +} +#endif + +/* this needs more info. subs in the data? control/adr? */ +#warning sector_identify needs to be written +int sector_identify(unsigned char *data) +{ + scramble(data); +/* +check mode byte for 1 or 2 +test parity to see if it's a valid sector +if invalid, return BURN_MODE_AUDIO; +else return mode byte (what about mode 2 formless? heh) +*/ + return BURN_MODE1; +} diff --git a/libburn/sector.h b/libburn/sector.h new file mode 100644 index 0000000..91b68d3 --- /dev/null +++ b/libburn/sector.h @@ -0,0 +1,31 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SECTOR +#define __SECTOR + +#include "libburn.h" +#include "transport.h" + +struct burn_drive; +struct isrc; + +int dec_to_bcd(int); + +int sector_toc(struct burn_write_opts *, int mode); +int sector_pregap(struct burn_write_opts *, unsigned char tno, + unsigned char control, int mode); +int sector_postgap(struct burn_write_opts *, unsigned char tno, + unsigned char control, int mode); +int sector_lout(struct burn_write_opts *, unsigned char control, int mode); +int sector_data(struct burn_write_opts *, struct burn_track *t, int psub); +void sector_headers(struct burn_write_opts *, unsigned char *, + int mode, int leadin); +void subcode_user(struct burn_write_opts *, unsigned char *s, + unsigned char tno, unsigned char control, + unsigned char index, struct isrc *isrc, int psub); + +int sector_identify(unsigned char *); + +void process_q(struct burn_drive *d, unsigned char *q); + +#endif /* __SECTOR */ diff --git a/libburn/sg.c b/libburn/sg.c new file mode 100644 index 0000000..cbcbf1e --- /dev/null +++ b/libburn/sg.c @@ -0,0 +1,334 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "transport.h" +#include "drive.h" +#include "sg.h" +#include "spc.h" +#include "mmc.h" +#include "sbc.h" +#include "debug.h" +#include "toc.h" +#include "util.h" + +static void enumerate_common(char *fname); + +static int sgio_test(int fd) +{ + unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 }; + sg_io_hdr_t s; + + memset(&s, 0, sizeof(sg_io_hdr_t)); + s.interface_id = 'S'; + s.dxfer_direction = SG_DXFER_NONE; + s.cmd_len = 6; + s.cmdp = test_ops; + s.timeout = 12345; + return ioctl(fd, SG_IO, &s); +} + +void ata_enumerate(void) +{ + struct hd_driveid tm; + int i, fd; + char fname[10]; + + for (i = 0; i < 26; i++) { + sprintf(fname, "/dev/hd%c", 'a' + i); + /* open O_RDWR so we don't think read only drives are + in some way useful + */ + fd = open(fname, O_RDWR | O_NONBLOCK); + if (fd == -1) + continue; + + /* found a drive */ + ioctl(fd, HDIO_GET_IDENTITY, &tm); + + /* not atapi */ + if (!(tm.config & 0x8000) || (tm.config & 0x4000)) { + close(fd); + continue; + } + + /* if SG_IO fails on an atapi device, we should stop trying to + use hd* devices */ + if (sgio_test(fd) == -1) { + close(fd); + return; + } + close(fd); + enumerate_common(fname); + } +} + +void sg_enumerate(void) +{ + struct sg_scsi_id sid; + int i, fd; + char fname[10]; + + for (i = 0; i < 32; i++) { + sprintf(fname, "/dev/sg%d", i); + /* open RDWR so we don't accidentally think read only drives + are in some way useful + */ + fd = open(fname, O_RDWR); + if (fd == -1) + continue; + + /* found a drive */ + ioctl(fd, SG_GET_SCSI_ID, &sid); + close(fd); + if (sid.scsi_type != TYPE_ROM) + continue; + + enumerate_common(fname); + } +} + +static void enumerate_common(char *fname) +{ + struct burn_drive *t; + struct burn_drive out; + + out.devname = burn_strdup(fname); + out.fd = -1337; + + out.grab = sg_grab; + out.release = sg_release; + out.issue_command = sg_issue_command; + out.getcaps = spc_getcaps; + out.released = 1; + out.status = BURN_DISC_UNREADY; + + out.eject = sbc_eject; + out.load = sbc_load; + out.lock = spc_prevent; + out.unlock = spc_allow; + out.read_disc_info = spc_sense_write_params; + out.get_erase_progress = spc_get_erase_progress; + out.test_unit_ready = spc_test_unit_ready; + out.probe_write_modes = spc_probe_write_modes; + out.read_toc = mmc_read_toc; + out.write = mmc_write; + out.erase = mmc_erase; + out.read_sectors = mmc_read_sectors; + out.perform_opc = mmc_perform_opc; + out.set_speed = mmc_set_speed; + out.send_parameters = spc_select_error_params; + out.send_write_parameters = spc_select_write_params; + out.send_cue_sheet = mmc_send_cue_sheet; + out.sync_cache = mmc_sync_cache; + out.get_nwa = mmc_get_nwa; + out.close_disc = mmc_close_disc; + out.close_session = mmc_close_session; + out.idata = malloc(sizeof(struct scsi_inquiry_data)); + out.idata->valid = 0; + out.mdata = malloc(sizeof(struct scsi_mode_data)); + out.mdata->valid = 0; + memset(&out.params, 0, sizeof(struct params)); + t = burn_drive_register(&out); + +/* try to get the drive info */ + if (sg_grab(t)) { + burn_print(2, "getting drive info\n"); + t->getcaps(t); + t->unlock(t); + t->released = 1; + } else { + burn_print(2, "unable to grab new located drive\n"); + } + +} + +/* + we use the sg reference count to decide whether we can use the + drive or not. + if refcount is not one, drive is open somewhere else. +*/ +int sg_grab(struct burn_drive *d) +{ + int fd, count; + + fd = open(d->devname, O_RDWR | O_NONBLOCK); + assert(fd != -1337); + if (-1 != fd) { +/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/ + count = 1; + if (1 == count) { + d->fd = fd; + fcntl(fd, F_SETOWN, getpid()); + d->released = 0; + return 1; + } + burn_print(1, "could not acquire drive - already open\n"); + close(fd); + return 0; + } + burn_print(1, "could not acquire drive\n"); + return 0; +} + +/* + non zero return means you still have the drive and it's not + in a state to be released? (is that even possible?) +*/ + +int sg_release(struct burn_drive *d) +{ + if (d->fd < 1) { + burn_print(1, "release an ungrabbed drive. die\n"); + return 0; + } + close(d->fd); + d->fd = -1337; + return 0; +} + +int sg_issue_command(struct burn_drive *d, struct command *c) +{ + int done = 0; + int err; + sg_io_hdr_t s; + + c->error = 0; +/* +this is valid during the mode probe in scan + if (d->fd < 1 || d->released) { + burn_print(1, + "command issued on ungrabbed drive, chaos.\n"); + burn_print(1, "fd = %d, released = %d\n", d->fd, + d->released); + } +*/ + memset(&s, 0, sizeof(sg_io_hdr_t)); + + s.interface_id = 'S'; + + if (c->dir == TO_DRIVE) + s.dxfer_direction = SG_DXFER_TO_DEV; + else if (c->dir == FROM_DRIVE) + s.dxfer_direction = SG_DXFER_FROM_DEV; + else if (c->dir == NO_TRANSFER) { + s.dxfer_direction = SG_DXFER_NONE; + assert(!c->page); + } + s.cmd_len = c->oplen; + s.cmdp = c->opcode; + s.mx_sb_len = 32; + s.sbp = c->sense; + memset(c->sense, 0, sizeof(c->sense)); + s.timeout = 200000; + if (c->page) { + s.dxferp = c->page->data; + if (c->dir == FROM_DRIVE) { + s.dxfer_len = BUFFER_SIZE; +/* touch page so we can use valgrind */ + memset(c->page->data, 0, BUFFER_SIZE); + } else { + assert(c->page->bytes > 0); + s.dxfer_len = c->page->bytes; + } + } else { + s.dxferp = NULL; + s.dxfer_len = 0; + } + s.usr_ptr = c; + + do { + err = ioctl(d->fd, SG_IO, &s); + assert(err != -1); + if (s.sb_len_wr) { + if (!c->retry) { + c->error = 1; + return 1; + } + switch (scsi_error(d, s.sbp, s.sb_len_wr)) { + case RETRY: + done = 0; + break; + case FAIL: + done = 1; + c->error = 1; + break; + } + } else { + done = 1; + } + } while (!done); + return 1; +} + +enum response scsi_error(struct burn_drive *d, unsigned char *sense, + int senselen) +{ + int key, asc, ascq; + + senselen = senselen; + key = sense[2]; + asc = sense[12]; + ascq = sense[13]; + + burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n", + key, asc, ascq, d->idata->vendor, d->idata->product); + + switch (asc) { + case 0: + burn_print(12, "NO ERROR!\n"); + return RETRY; + + case 2: + burn_print(1, "not ready\n"); + return RETRY; + case 4: + burn_print(1, + "logical unit is in the process of becoming ready\n"); + return RETRY; + case 0x20: + if (key == 5) + burn_print(1, "bad opcode\n"); + return FAIL; + case 0x21: + burn_print(1, "invalid address or something\n"); + return FAIL; + case 0x24: + if (key == 5) + burn_print(1, "invalid field in cdb\n"); + else + break; + return FAIL; + case 0x26: + if ( key == 5 ) + burn_print( 1, "invalid field in parameter list\n" ); + return FAIL; + case 0x28: + if (key == 6) + burn_print(1, + "Not ready to ready change, medium may have changed\n"); + else + break; + return RETRY; + case 0x3A: + burn_print(12, "Medium not present in %s %s\n", + d->idata->vendor, d->idata->product); + + d->status = BURN_DISC_EMPTY; + return FAIL; + } + burn_print(1, "unknown failure\n"); + burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq); + return FAIL; +} diff --git a/libburn/sg.h b/libburn/sg.h new file mode 100644 index 0000000..8f431c5 --- /dev/null +++ b/libburn/sg.h @@ -0,0 +1,19 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SG +#define __SG + +struct burn_drive; +struct command; + +enum response +{ RETRY, FAIL }; + +void sg_enumerate(void); +void ata_enumerate(void); +int sg_grab(struct burn_drive *); +int sg_release(struct burn_drive *); +int sg_issue_command(struct burn_drive *, struct command *); +enum response scsi_error(struct burn_drive *, unsigned char *, int); + +#endif /* __SG */ diff --git a/libburn/source.c b/libburn/source.c new file mode 100644 index 0000000..fd0c56b --- /dev/null +++ b/libburn/source.c @@ -0,0 +1,36 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include "libburn.h" +#include "source.h" +#include "structure.h" + +void burn_source_free(struct burn_source *src) +{ + if (--src->refcount < 1) { + if (src->free_data) + src->free_data(src); + free(src); + } +} + +enum burn_source_status burn_track_set_source(struct burn_track *t, + struct burn_source *s) +{ + if (!s->read) + return BURN_SOURCE_FAILED; + s->refcount++; + t->source = s; + return BURN_SOURCE_OK; +} + +struct burn_source *burn_source_new(void) +{ + struct burn_source *out; + + out = malloc(sizeof(struct burn_source)); + memset(out, 0, sizeof(struct burn_source)); + out->refcount = 1; + return out; +} diff --git a/libburn/source.h b/libburn/source.h new file mode 100644 index 0000000..e0a69aa --- /dev/null +++ b/libburn/source.h @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SOURCE +#define __SOURCE + +struct burn_source *burn_source_new(void); + +#endif /*__SOURCE*/ diff --git a/libburn/spc.c b/libburn/spc.c new file mode 100644 index 0000000..89fd5de --- /dev/null +++ b/libburn/spc.c @@ -0,0 +1,405 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* scsi primary commands */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "transport.h" +#include "spc.h" +#include "mmc.h" +#include "sbc.h" +#include "drive.h" +#include "debug.h" +#include "options.h" + +/* spc command set */ +static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 }; + +/*static char SPC_TEST[]={0,0,0,0,0,0};*/ +static unsigned char SPC_PREVENT[] = { 0x1e, 0, 0, 0, 1, 0 }; +static unsigned char SPC_ALLOW[] = { 0x1e, 0, 0, 0, 0, 0 }; +static unsigned char SPC_MODE_SENSE[] = { 0x5a, 0, 0, 0, 0, 0, 0, 16, 0, 0 }; +static unsigned char SPC_MODE_SELECT[] = + { 0x55, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char SPC_REQUEST_SENSE[] = { 0x03, 0, 0, 0, 18, 0 }; +static unsigned char SPC_TEST_UNIT_READY[] = { 0x00, 0, 0, 0, 0, 0 }; + +int spc_test_unit_ready(struct burn_drive *d) +{ + struct command c; + + c.retry = 0; + c.oplen = sizeof(SPC_TEST_UNIT_READY); + memcpy(c.opcode, SPC_TEST_UNIT_READY, sizeof(SPC_TEST_UNIT_READY)); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); + if (c.error) + return (c.sense[2] & 0xF) == 0; + return 1; +} + +void spc_request_sense(struct burn_drive *d, struct buffer *buf) +{ + struct command c; + + c.retry = 0; + c.oplen = sizeof(SPC_REQUEST_SENSE); + memcpy(c.opcode, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE)); + c.page = buf; + c.page->sectors = 0; + c.page->bytes = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); +} + +int spc_get_erase_progress(struct burn_drive *d) +{ + struct buffer b; + + spc_request_sense(d, &b); + return (b.data[16] << 8) | b.data[17]; +} + +void spc_inquiry(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_inquiry_data *id; + struct command c; + + memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY)); + c.retry = 1; + c.oplen = sizeof(SPC_INQUIRY); + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + id = (struct scsi_inquiry_data *)d->idata; + id->vendor[8] = 0; + id->product[16] = 0; + id->revision[4] = 0; + + memcpy(id->vendor, c.page->data + 8, 8); + memcpy(id->product, c.page->data + 16, 16); + memcpy(id->revision, c.page->data + 32, 4); + + id->valid = 1; + return; +} + +void spc_prevent(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SPC_PREVENT, sizeof(SPC_PREVENT)); + c.retry = 1; + c.oplen = sizeof(SPC_PREVENT); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void spc_allow(struct burn_drive *d) +{ + struct command c; + + memcpy(c.opcode, SPC_ALLOW, sizeof(SPC_ALLOW)); + c.retry = 1; + c.oplen = sizeof(SPC_ALLOW); + c.page = NULL; + c.dir = NO_TRANSFER; + d->issue_command(d, &c); +} + +void spc_sense_caps(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size; + unsigned char *page; + struct command c; + + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SENSE); + c.opcode[2] = 0x2A; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + size = c.page->data[0] * 256 + c.page->data[1]; + m = d->mdata; + page = c.page->data + 8; + + m->buffer_size = page[12] * 256 + page[13]; + m->dvdram_read = page[2] & 32; + m->dvdram_write = page[3] & 32; + m->dvdr_read = page[2] & 16; + m->dvdr_write = page[3] & 16; + m->dvdrom_read = page[2] & 8; + m->simulate = page[3] & 4; + m->cdrw_read = page[2] & 2; + m->cdrw_write = page[3] & 2; + m->cdr_read = page[2] & 1; + m->cdr_write = page[3] & 1; + m->max_read_speed = page[8] * 256 + page[9]; + m->cur_read_speed = page[14] * 256 + page[15]; + m->max_write_speed = page[18] * 256 + page[19]; + m->cur_write_speed = page[20] * 256 + page[21]; + m->c2_pointers = page[5] & 16; + m->valid = 1; + m->underrun_proof = page[4] & 128; +} + +void spc_sense_error_params(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size; + unsigned char *page; + struct command c; + + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SENSE); + c.opcode[2] = 0x01; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + size = c.page->data[0] * 256 + c.page->data[1]; + m = d->mdata; + page = c.page->data + 8; + d->params.retries = page[3]; + m->retry_page_length = page[1]; + m->retry_page_valid = 1; +} + +void spc_select_error_params(struct burn_drive *d, + const struct burn_read_opts *o) +{ + struct buffer buf; + struct command c; + + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SELECT); + c.opcode[8] = 8 + 2 + d->mdata->retry_page_length; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + assert(d->mdata->valid); + memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length); + c.page->bytes = 8 + 2 + d->mdata->retry_page_length; + c.page->data[8] = 1; + c.page->data[9] = d->mdata->retry_page_length; + if (o->transfer_damaged_blocks) + c.page->data[10] |= 32; + if (o->report_recovered_errors) + c.page->data[10] |= 4; + if (!o->hardware_error_recovery) + c.page->data[10] |= 1; +/*burn_print(1, "error parameter 0x%x\n", c->page->data[10]);*/ + c.page->data[11] = d->params.retries; + c.dir = TO_DRIVE; + d->issue_command(d, &c); +} + +void spc_sense_write_params(struct burn_drive *d) +{ + struct buffer buf; + struct scsi_mode_data *m; + int size; + unsigned char *page; + struct command c; + + assert(d->mdata->cdr_write || d->mdata->cdrw_write || + d->mdata->dvdr_write || d->mdata->dvdram_write); + + memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SENSE); + c.opcode[2] = 0x05; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + c.dir = FROM_DRIVE; + d->issue_command(d, &c); + + size = c.page->data[0] * 256 + c.page->data[1]; + m = d->mdata; + page = c.page->data + 8; + burn_print(1, "write page length 0x%x\n", page[1]); + m->write_page_length = page[1]; + m->write_page_valid = 1; + mmc_read_disc_info(d); +} + +void spc_select_write_params(struct burn_drive *d, + const struct burn_write_opts *o) +{ + struct buffer buf; + struct command c; + int bufe, sim; + + assert(o->drive == d); + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SELECT); + c.opcode[8] = 8 + 2 + d->mdata->write_page_length; + c.page = &buf; + c.page->bytes = 0; + c.page->sectors = 0; + assert(d->mdata->valid); + memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length); + c.page->bytes = 8 + 2 + d->mdata->write_page_length; + c.page->data[8] = 5; + c.page->data[9] = d->mdata->write_page_length; + + burn_print(12, "using write page length %d (valid %d)\n", + d->mdata->write_page_length, d->mdata->write_page_valid); + bufe = o->underrun_proof; + sim = o->simulate; + c.page->data[10] = (bufe << 6) + + (sim << 4) + + o->write_type; + c.page->data[11] = (o->multi << 6) | o->control; + c.page->data[12] = spc_block_type(o->block_type); + c.page->data[22] = 0; + c.page->data[23] = 150; /* audio pause length */ +/*XXX need session format! */ + c.dir = TO_DRIVE; + d->issue_command(d, &c); +} + +void spc_getcaps(struct burn_drive *d) +{ + spc_inquiry(d); + spc_sense_caps(d); + spc_sense_error_params(d); +} + +/* +only called when a blank is present, so we set type to blank +(on the last pass) + +don't check totally stupid modes (raw/raw0) +some drives say they're ok, and they're not. +*/ + +void spc_probe_write_modes(struct burn_drive *d) +{ + struct buffer buf; + int try_write_type = 1; + int try_block_type = 0; + int key, asc, ascq; + struct command c; + + while (try_write_type != 4) { + burn_print(9, "trying %d, %d\n", try_write_type, + try_block_type); + memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT)); + c.retry = 1; + c.oplen = sizeof(SPC_MODE_SELECT); + c.opcode[8] = 8 + 2 + 0x32; + c.page = &buf; + + memset(c.page->data, 0, 8 + 2 + 0x32); + c.page->bytes = 8 + 2 + 0x32; + + c.page->data[8] = 5; + c.page->data[9] = 0x32; + c.page->data[10] = try_write_type; + if (try_block_type > 4) + c.page->data[11] = 4; + else + c.page->data[11] = 0; + c.page->data[12] = try_block_type; + c.page->data[23] = 150; + c.dir = TO_DRIVE; + d->issue_command(d, &c); + + key = c.sense[2]; + asc = c.sense[12]; + ascq = c.sense[13]; + + if (key) + burn_print(7, "%d not supported\n", try_block_type); + else { + burn_print(7, "%d:%d SUPPORTED MODE!\n", + try_write_type, try_block_type); + if (try_write_type == 2) /* sao */ + d->block_types[try_write_type] = + BURN_BLOCK_SAO; + else + d->block_types[try_write_type] |= + 1 << try_block_type; + } + switch (try_block_type) { + case 0: + case 1: + case 2: + try_block_type++; + break; + case 3: + try_block_type = 8; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + try_block_type++; + break; + case 13: + try_block_type = 0; + try_write_type++; + break; + default: + return; + } + } +} + +int spc_block_type(enum burn_block_types b) +{ + switch (b) { + case BURN_BLOCK_SAO: + return 0; /* ignored bitz */ + case BURN_BLOCK_RAW0: + return 0; + case BURN_BLOCK_RAW16: + return 1; + case BURN_BLOCK_RAW96P: + return 2; + case BURN_BLOCK_RAW96R: + return 3; + case BURN_BLOCK_MODE1: + return 8; + case BURN_BLOCK_MODE2R: + return 9; + case BURN_BLOCK_MODE2_PATHETIC: + return 10; + case BURN_BLOCK_MODE2_LAME: + return 11; + case BURN_BLOCK_MODE2_OBSCURE: + return 12; + case BURN_BLOCK_MODE2_OK: + return 13; + } + assert(0); +} diff --git a/libburn/spc.h b/libburn/spc.h new file mode 100644 index 0000000..2bca4de --- /dev/null +++ b/libburn/spc.h @@ -0,0 +1,25 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __SPC +#define __SPC + +#include "libburn.h" + +void spc_inquiry(struct burn_drive *); +void spc_prevent(struct burn_drive *); +void spc_allow(struct burn_drive *); +void spc_sense_caps(struct burn_drive *); +void spc_sense_error_params(struct burn_drive *); +void spc_select_error_params(struct burn_drive *, + const struct burn_read_opts *); +void spc_getcaps(struct burn_drive *d); +void spc_sense_write_params(struct burn_drive *); +void spc_select_write_params(struct burn_drive *, + const struct burn_write_opts *); +void spc_probe_write_modes(struct burn_drive *); +void spc_request_sense(struct burn_drive *d, struct buffer *buf); +int spc_block_type(enum burn_block_types b); +int spc_get_erase_progress(struct burn_drive *d); +int spc_test_unit_ready(struct burn_drive *d); + +#endif /*__SPC*/ diff --git a/libburn/structure.c b/libburn/structure.c new file mode 100644 index 0000000..6719ab2 --- /dev/null +++ b/libburn/structure.c @@ -0,0 +1,305 @@ +#include +#include +#include +#include +#include "libburn.h" +#include "structure.h" +#include "write.h" +#include "debug.h" + +#define RESIZE(TO, NEW, pos) {\ + void *tmp;\ +\ + assert(!(pos > BURN_POS_END));\ + if (pos == BURN_POS_END)\ + pos = TO->NEW##s;\ + if (pos > TO->NEW##s)\ + return 0;\ +\ + tmp = realloc(TO->NEW, sizeof(struct NEW *) * (TO->NEW##s + 1));\ + if (!tmp)\ + return 0;\ + TO->NEW = tmp;\ + memmove(TO->NEW + pos + 1, TO->NEW + pos,\ + sizeof(struct NEW *) * (TO->NEW##s - pos));\ + TO->NEW##s++;\ +} + +struct burn_disc *burn_disc_create(void) +{ + struct burn_disc *d; + d = malloc(sizeof(struct burn_disc)); + memset(d, 0, sizeof(struct burn_disc)); + d->refcnt = 1; + d->sessions = 0; + d->session = NULL; + return d; +} + +void burn_disc_free(struct burn_disc *d) +{ + d->refcnt--; + if (d->refcnt == 0) { + /* dec refs on all elements */ + int i; + + for (i = 0; i < d->sessions; i++) + burn_session_free(d->session[i]); + free(d->session); + free(d); + } +} + +struct burn_session *burn_session_create(void) +{ + struct burn_session *s; + s = malloc(sizeof(struct burn_session)); + memset(s, 0, sizeof(struct burn_session)); + s->refcnt = 1; + s->tracks = 0; + s->track = NULL; + s->hidefirst = 0; + return s; +} + +void burn_session_hide_first_track(struct burn_session *s, int onoff) +{ + s->hidefirst = onoff; +} + +void burn_session_free(struct burn_session *s) +{ + s->refcnt--; + if (s->refcnt == 0) { + /* dec refs on all elements */ + int i; + + for (i = 0; i < s->tracks; i++) + burn_track_free(s->track[i]); + free(s->track); + free(s); + } + +} + +int burn_disc_add_session(struct burn_disc *d, struct burn_session *s, + unsigned int pos) +{ + RESIZE(d, session, pos); + d->session[pos] = s; + s->refcnt++; + return 1; +} + +struct burn_track *burn_track_create(void) +{ + struct burn_track *t; + t = malloc(sizeof(struct burn_track)); + memset(t, 0, sizeof(struct burn_track)); + t->refcnt = 1; + t->indices = 0; + t->offset = 0; + t->offsetcount = 0; + t->tail = 0; + t->tailcount = 0; + t->mode = BURN_MODE1; + t->isrc.has_isrc = 0; + t->pad = 1; + t->entry = NULL; + t->source = NULL; + t->postgap = 0; + t->pregap1 = 0; + t->pregap2 = 0; + return t; +} + +void burn_track_free(struct burn_track *t) +{ + t->refcnt--; + if (t->refcnt == 0) { + /* dec refs on all elements */ + if (t->source) + burn_source_free(t->source); + free(t); + } +} + +int burn_session_add_track(struct burn_session *s, struct burn_track *t, + unsigned int pos) +{ + RESIZE(s, track, pos); + s->track[pos] = t; + t->refcnt++; + return 1; +} + +int burn_session_remove_track(struct burn_session *s, struct burn_track *t) +{ + struct burn_track **tmp; + int i, pos = -1; + + assert(s->track != NULL); + + burn_track_free(t); + + /* Find the position */ + for (i = 0; i < s->tracks; i++) { + if (t == s->track[i]) + pos = i; + } + + if (pos == -1) + return 0; + + /* Is it the last track? */ + if (pos != s->tracks) { + memmove(s->track[pos], s->track[pos + 1], + sizeof(struct burn_track *) * (s->tracks - (pos + 1))); + } + + s->tracks--; + tmp = realloc(s->track, sizeof(struct burn_track *) * s->tracks); + if (!tmp) + return 0; + s->track = tmp; + return 1; +} + +void burn_structure_print_disc(struct burn_disc *d) +{ + int i; + + burn_print(12, "This disc has %d sessions\n", d->sessions); + for (i = 0; i < d->sessions; i++) { + burn_structure_print_session(d->session[i]); + } +} +void burn_structure_print_session(struct burn_session *s) +{ + int i; + + burn_print(12, " Session has %d tracks\n", s->tracks); + for (i = 0; i < s->tracks; i++) { + burn_structure_print_track(s->track[i]); + } +} +void burn_structure_print_track(struct burn_track *t) +{ + burn_print(12, "(%p) track size %d sectors\n", t, + burn_track_get_sectors(t)); +} + +void burn_track_define_data(struct burn_track *t, int offset, int tail, + int pad, int mode) +{ + t->offset = offset; + t->pad = pad; + t->mode = mode; + t->tail = tail; +} + +void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, + unsigned char year, unsigned int serial) +{ + int i; + + t->isrc.has_isrc = 1; + for (i = 0; i < 2; ++i) { + assert((country[i] >= '0' || country[i] < '9') && + (country[i] >= 'a' || country[i] < 'z') && + (country[i] >= 'A' || country[i] < 'Z')); + t->isrc.country[i] = country[i]; + } + for (i = 0; i < 3; ++i) { + assert((owner[i] >= '0' || owner[i] < '9') && + (owner[i] >= 'a' || owner[i] < 'z') && + (owner[i] >= 'A' || owner[i] < 'Z')); + t->isrc.owner[i] = owner[i]; + } + assert(year <= 99); + t->isrc.year = year; + assert(serial <= 99999); + t->isrc.serial = serial; +} + +void burn_track_clear_isrc(struct burn_track *t) +{ + t->isrc.has_isrc = 0; +} + +int burn_track_get_sectors(struct burn_track *t) +{ + int size; + int sectors, seclen; + + seclen = burn_sector_length(t->mode); + size = t->offset + t->source->get_size(t->source) + t->tail; + sectors = size / seclen; + if (size % seclen) + sectors++; + burn_print(1, "%d sectors of %d length\n", sectors, seclen); + return sectors; +} + +int burn_track_get_shortage(struct burn_track *t) +{ + int size; + int seclen; + + seclen = burn_sector_length(t->mode); + size = t->offset + t->source->get_size(t->source) + t->tail; + if (size % seclen) + return seclen - size % seclen; + return 0; +} + +int burn_session_get_sectors(struct burn_session *s) +{ + int sectors = 0, i; + + for (i = 0; i < s->tracks; i++) + sectors += burn_track_get_sectors(s->track[i]); + return sectors; +} + +int burn_disc_get_sectors(struct burn_disc *d) +{ + int sectors = 0, i; + + for (i = 0; i < d->sessions; i++) + sectors += burn_session_get_sectors(d->session[i]); + return sectors; +} + +void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry) +{ + memcpy(entry, t->entry, sizeof(struct burn_toc_entry)); +} + +void burn_session_get_leadout_entry(struct burn_session *s, + struct burn_toc_entry *entry) +{ + memcpy(entry, s->leadout_entry, sizeof(struct burn_toc_entry)); +} + +struct burn_session **burn_disc_get_sessions(struct burn_disc *d, int *num) +{ + *num = d->sessions; + return d->session; +} + +struct burn_track **burn_session_get_tracks(struct burn_session *s, int *num) +{ + *num = s->tracks; + return s->track; +} + +int burn_track_get_mode(struct burn_track *track) +{ + return track->mode; +} + +int burn_session_get_hidefirst(struct burn_session *session) +{ + return session->hidefirst; +} diff --git a/libburn/structure.h b/libburn/structure.h new file mode 100644 index 0000000..61f083f --- /dev/null +++ b/libburn/structure.h @@ -0,0 +1,70 @@ +#ifndef BURN__STRUCTURE_H +#define BURN__STRUCTURE_H + +struct isrc +{ + int has_isrc; + char country[2]; /* each must be 0-9, A-Z */ + char owner[3]; /* each must be 0-9, A-Z */ + unsigned char year; /* must be 0-99 */ + unsigned int serial; /* must be 0-99999 */ +}; + +struct burn_track +{ + int refcnt; + struct burn_toc_entry *entry; + unsigned char indices; + /* lba address of the index */ + unsigned int index[99]; + /** number of 0 bytes to write before data */ + int offset; + /** how much offset has been used */ + int offsetcount; + /** Number of zeros to write after data */ + int tail; + /** how much tail has been used */ + int tailcount; + /** 1 means Pad with zeros, 0 means start reading the next track */ + int pad; + /** Data source */ + struct burn_source *source; + /** End of Source flag */ + int eos; + /** The audio/data mode for the entry. Derived from control and + possibly from reading the track's first sector. */ + int mode; + /** The track contains interval one of a pregap */ + int pregap1; + /** The track contains interval two of a pregap */ + int pregap2; + /** The track contains a postgap */ + int postgap; + struct isrc isrc; +}; + +struct burn_session +{ + unsigned char firsttrack; + unsigned char lasttrack; + int hidefirst; + unsigned char start_m; + unsigned char start_s; + unsigned char start_f; + struct burn_toc_entry *leadout_entry; + + int tracks; + struct burn_track **track; + int refcnt; +}; + +struct burn_disc +{ + int sessions; + struct burn_session **session; + int refcnt; +}; + +int burn_track_get_shortage(struct burn_track *t); + +#endif /* BURN__STRUCTURE_H */ diff --git a/libburn/toc.c b/libburn/toc.c new file mode 100644 index 0000000..8d0d3fd --- /dev/null +++ b/libburn/toc.c @@ -0,0 +1,130 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include "toc.h" +#include "transport.h" +#include "libburn.h" +#include "sector.h" +#include "options.h" + +#if 0 +static void write_clonecd2(volatile struct toc *toc, int f); + +static void write_clonecd2(volatile struct toc *toc, int f) +{ + int i; + + /* header */ + dprintf(f, "[CloneCD]\r\n"); + dprintf(f, "Version=2\r\n"); + dprintf(f, "\r\n"); + + /* disc data */ + dprintf(f, "[Disc]\r\n"); + + dprintf(f, "TocEntries=%d\r\n", toc->toc_entries); + dprintf(f, "Sessions=%d\r\n", toc->sessions); + dprintf(f, "DataTracksScrambled=%d\r\n", toc->datatracksscrambled); + dprintf(f, "CDTextLength=%d\r\n", toc->cdtextlength); + dprintf(f, "\r\n"); + + /* session data */ + for (i = 0; i < toc->sessions; ++i) { + dprintf(f, "[Session %d]\r\n", i + 1); + + { + int m; + + switch (toc->session[i].track[0]->mode) { + case BURN_MODE_RAW_DATA: + case BURN_MODE_AUDIO: + m = 0; + break; + case BURN_MODE0: + m = 1; + break; + case BURN_MODE1: + case BURN_MODE2_FORMLESS: + case BURN_MODE2_FORM1: + case BURN_MODE2_FORM2: + case BURN_MODE_UNINITIALIZED: + assert(0); /* unhandled! find out ccd's + value for these modes! */ + } + dprintf(f, "PreGapMode=%d\r\n", m); + } + dprintf(f, "\r\n"); + } + + for (i = 0; i < toc->toc_entries; ++i) { + dprintf(f, "[Entry %d]\r\n", i); + + dprintf(f, "Session=%d\r\n", toc->toc_entry[i].session); + dprintf(f, "Point=0x%02x\r\n", toc->toc_entry[i].point); + dprintf(f, "ADR=0x%02x\r\n", toc->toc_entry[i].adr); + dprintf(f, "Control=0x%02x\r\n", toc->toc_entry[i].control); + dprintf(f, "TrackNo=%d\r\n", toc->toc_entry[i].tno); + dprintf(f, "AMin=%d\r\n", toc->toc_entry[i].min); + dprintf(f, "ASec=%d\r\n", toc->toc_entry[i].sec); + dprintf(f, "AFrame=%d\r\n", toc->toc_entry[i].frame); + dprintf(f, "ALBA=%d\r\n", + burn_msf_to_lba(toc->toc_entry[i].min, + toc->toc_entry[i].sec, + toc->toc_entry[i].frame)); + dprintf(f, "Zero=%d\r\n", toc->toc_entry[i].zero); + dprintf(f, "PMin=%d\r\n", toc->toc_entry[i].pmin); + dprintf(f, "PSec=%d\r\n", toc->toc_entry[i].psec); + dprintf(f, "PFrame=%d\r\n", toc->toc_entry[i].pframe); + dprintf(f, "PLBA=%d\r\n", + burn_msf_to_lba(toc->toc_entry[i].pmin, + toc->toc_entry[i].psec, + toc->toc_entry[i].pframe)); + dprintf(f, "\r\n"); + } +} +#endif + +void toc_find_modes(struct burn_drive *d) +{ + struct burn_read_opts o; + int lba; + int i, j; + struct buffer mem; + struct burn_toc_entry *e; + + assert(d->busy); + + mem.bytes = 0; + mem.sectors = 1; + o.raw = 1; + o.c2errors = 0; + o.subcodes_audio = 1; + o.subcodes_data = 1; + o.hardware_error_recovery = 1; + o.report_recovered_errors = 0; + o.transfer_damaged_blocks = 1; + o.hardware_error_retries = 1; + + for (i = 0; i < d->disc->sessions; i++) + for (j = 0; j < d->disc->session[i]->tracks; j++) { + struct burn_track *t = d->disc->session[i]->track[j]; + + e = t->entry; + if (!e) + lba = 0; + else + lba = burn_msf_to_lba(e->pmin, e->psec, + e->pframe); +/* XXX | in the subcodes if appropriate! */ + if (e && !(e->control & 4)) { + t->mode = BURN_AUDIO; + } else { + mem.sectors = 1; + d->read_sectors(d, lba, mem.sectors, &o, &mem); + t->mode = sector_identify(mem.data); + } + } +} diff --git a/libburn/toc.h b/libburn/toc.h new file mode 100644 index 0000000..1d804e7 --- /dev/null +++ b/libburn/toc.h @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __TOC_H +#define __TOC_H + +struct command; + +#include "libburn.h" +#include "structure.h" + +/* return if a given entry refers to a track position */ +#define TOC_ENTRY_IS_TRACK(drive, entrynum) \ + ((drive)->toc_entry[entrynum].point < 100) + +/* return if a given entry is in audio or data format */ +#define TOC_ENTRY_IS_AUDIO(drive, entrynum) \ + (~(drive)->toc_entry[entrynum].control & 4) + +/* return the point value for a given entry number */ +#define TOC_POINT(drive, entrynum) ((drive)->toc_entry[entrynum].point) + +/* return the track struct for a given entry number */ +#define TOC_TRACK(drive, entrynum) \ + ((drive)->track[TOC_POINT(drive, entrynum) - 1]) + +/* return the lba of a toc entry */ +#define TOC_ENTRY_PLBA(drive, entrynum) \ + burn_msf_to_lba((drive)->toc_entry[(entrynum)].pmin, \ + (drive)->toc_entry[(entrynum)].psec, \ + (drive)->toc_entry[(entrynum)].pframe) + +/* flags for the q subchannel control field */ +#define TOC_CONTROL_AUDIO (0) +#define TOC_CONTROL_DATA (1 << 2) +#define TOC_CONTROL_AUDIO_TWO_CHANNELS (0) +#define TOC_CONTROL_AUDIO_FOUR_CHANNELS (1 << 3) +#define TOC_CONTROL_AUDIO_PRE_EMPHASIS (1 << 0) +#define TOC_CONTROL_DATA_RECORDED_UNINTERRUPTED (0) +#define TOC_CONTROL_DATA_RECORDED_INCREMENT (1 << 0) +#define TOC_CONTROL_COPY_PROHIBITED (0) +#define TOC_CONTROL_COPY_PERMITTED (1 << 1) + +/** read a sector from each track on disc to determine modes + @param d The drive. +*/ +void toc_find_modes(struct burn_drive *d); + +#endif /*__TOC_H*/ diff --git a/libburn/transport.h b/libburn/transport.h new file mode 100644 index 0000000..2f7ee1a --- /dev/null +++ b/libburn/transport.h @@ -0,0 +1,162 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __TRANSPORT +#define __TRANSPORT + +#include "libburn.h" + +#include +/* sg data structures */ +#include +#include +#include + +/* kludge! glibc headers don't define all the SCSI shit that we use! */ +#ifndef SG_GET_ACCESS_COUNT +# define SG_GET_ACCESS_COUNT 0x2289 +#endif + +#define BUFFER_SIZE 65536 + +enum transfer_direction +{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER }; + +/* end of sg data structures */ + +/* generic 'drive' data structures */ + +struct cue_sheet +{ + int count; + unsigned char *data; +}; + +struct params +{ + int speed; + int retries; +}; + +struct buffer +{ + unsigned char data[BUFFER_SIZE]; + int sectors; + int bytes; +}; + +struct command +{ + unsigned char opcode[16]; + int oplen; + int dir; + unsigned char sense[128]; + int error; + int retry; + struct buffer *page; +}; + +struct scsi_inquiry_data +{ + char vendor[9]; + char product[17]; + char revision[5]; + int valid; +}; + +struct scsi_mode_data +{ + int buffer_size; + int dvdram_read; + int dvdram_write; + int dvdr_read; + int dvdr_write; + int dvdrom_read; + int cdrw_read; + int cdrw_write; + int cdr_read; + int cdr_write; + int simulate; + int max_read_speed; + int max_write_speed; + int cur_read_speed; + int cur_write_speed; + int retry_page_length; + int retry_page_valid; + int write_page_length; + int write_page_valid; + int c2_pointers; + int valid; + int underrun_proof; +}; + +struct burn_drive +{ + int host; + int id; + int channel; + int lun; + char *devname; + int fd; + + pthread_mutex_t access_lock; + + enum burn_disc_status status; + int erasable; + volatile int released; + int nwa; /* next writeable address */ + int alba; /* absolute lba */ + int rlba; /* relative lba in section */ + int start_lba; + int end_lba; + int toc_temp; + struct burn_disc *disc; /* disc structure */ + int block_types[4]; + struct buffer *buffer; + struct burn_progress progress; + + volatile int cancel; + volatile enum burn_drive_status busy; +/* transport functions */ + int (*grab) (struct burn_drive *); + int (*release) (struct burn_drive *); + int (*issue_command) (struct burn_drive *, struct command *); + +/* lower level functions */ + void (*erase) (struct burn_drive *, int); + void (*getcaps) (struct burn_drive *); + int (*write) (struct burn_drive *, int, struct buffer *); + void (*read_toc) (struct burn_drive *); + void (*lock) (struct burn_drive *); + void (*unlock) (struct burn_drive *); + void (*eject) (struct burn_drive *); + void (*load) (struct burn_drive *); + void (*read_disc_info) (struct burn_drive *); + void (*read_sectors) (struct burn_drive *, + int start, + int len, + const struct burn_read_opts *, struct buffer *); + void (*perform_opc) (struct burn_drive *); + void (*set_speed) (struct burn_drive *, int, int); + void (*send_parameters) (struct burn_drive *, + const struct burn_read_opts *); + void (*send_write_parameters) (struct burn_drive *, + const struct burn_write_opts *); + void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *); + void (*sync_cache) (struct burn_drive *); + int (*get_erase_progress) (struct burn_drive *); + int (*get_nwa) (struct burn_drive *); + void (*close_disc) (struct burn_drive * d, struct burn_write_opts * o); + void (*close_session) (struct burn_drive * d, + struct burn_write_opts * o); + int (*test_unit_ready) (struct burn_drive * d); + void (*probe_write_modes) (struct burn_drive * d); + struct params params; + struct scsi_inquiry_data *idata; + struct scsi_mode_data *mdata; + int toc_entries; + struct burn_toc_entry *toc_entry; +}; + +/* end of generic 'drive' data structures */ + +#endif /* __TRANSPORT */ diff --git a/libburn/util.c b/libburn/util.c new file mode 100644 index 0000000..cad2858 --- /dev/null +++ b/libburn/util.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include "../version.h" +#include "util.h" +#include "libburn.h" + +char *burn_strdup(char *s) +{ + char *ret; + int l; + + assert(s); + + l = strlen(s) + 1; + ret = malloc(l); + memcpy(ret, s, l); + + return ret; +} + +char *burn_strndup(char *s, int n) +{ + char *ret; + int l; + + assert(s); + assert(n > 0); + + l = strlen(s); + ret = malloc(l < n ? l : n); + + memcpy(ret, s, l < n - 1 ? l : n - 1); + ret[n - 1] = '\0'; + + return ret; +} + +void burn_version(int *major, int *minor, int *micro) +{ + *major = BURN_MAJOR_VERSION; + *minor = BURN_MINOR_VERSION; + *micro = BURN_MICRO_VERSION; +} diff --git a/libburn/util.h b/libburn/util.h new file mode 100644 index 0000000..4e74895 --- /dev/null +++ b/libburn/util.h @@ -0,0 +1,8 @@ +#ifndef __UTIL +#define __UTIL + +char *burn_strdup(char *s); + +char *burn_strndup(char *s, int n); + +#endif diff --git a/libburn/write.c b/libburn/write.c new file mode 100644 index 0000000..fca62b6 --- /dev/null +++ b/libburn/write.c @@ -0,0 +1,529 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "sector.h" +#include "libburn.h" +#include "drive.h" +#include "transport.h" +#include "message.h" +#include "crc.h" +#include "debug.h" +#include "init.h" +#include "lec.h" +#include "toc.h" +#include "util.h" +#include "sg.h" +#include "write.h" +#include "options.h" + +static int type_to_ctrl(int mode) +{ + int ctrl = 0; + + int data = BURN_MODE2 | BURN_MODE1 | BURN_MODE0; + + if (mode & data) { + ctrl |= 4; + } else if (mode & BURN_AUDIO) { + if (mode & BURN_4CH) + ctrl |= 8; + if (mode & BURN_PREEMPHASIS) + ctrl |= 1; + } else + assert(0); + + if (mode & BURN_COPY) + ctrl |= 2; + + return ctrl; +} + +/* only the ctrl nibble is set here (not adr) */ +static void type_to_form(int mode, unsigned char *ctladr, int *form) +{ + *ctladr = type_to_ctrl(mode) << 4; + + if (mode & BURN_AUDIO) + *form = 0; + if (mode & BURN_MODE0) + assert(0); + if (mode & BURN_MODE1) + *form = 0x10; + if (mode & BURN_MODE2) + assert(0); /* XXX someone's gonna want this sometime */ + if (mode & BURN_MODE_RAW) + *form = 0; + if (mode & BURN_SUBCODE_P16) /* must be expanded to R96 */ + *form |= 0x40; + if (mode & BURN_SUBCODE_P96) + *form |= 0xC0; + if (mode & BURN_SUBCODE_R96) + *form |= 0x40; +} + +int burn_write_flush(struct burn_write_opts *o) +{ + struct burn_drive *d = o->drive; + + if (d->buffer->bytes && !d->cancel) { + int err; + err = d->write(d, d->nwa, d->buffer); + if (err == BE_CANCELLED) + return 0; + d->nwa += d->buffer->sectors; + } + d->sync_cache(d); + return 1; +} + +static void print_cue(struct cue_sheet *sheet) +{ + int i; + unsigned char *unit; + + printf("\n"); + printf("ctladr|trno|indx|form|scms| msf\n"); + printf("------+----+----+----+----+--------\n"); + for (i = 0; i < sheet->count; i++) { + unit = sheet->data + 8 * i; + printf(" %1X %1X | %02X | %02X | %02X | %02X |", + (unit[0] & 0xf0) >> 4, unit[0] & 0xf, unit[1], unit[2], + unit[3], unit[4]); + printf("%02X:%02X:%02X\n", unit[5], unit[6], unit[7]); + } +} + +static void add_cue(struct cue_sheet *sheet, unsigned char ctladr, + unsigned char tno, unsigned char indx, + unsigned char form, unsigned char scms, int lba) +{ + unsigned char *unit; + unsigned char *ptr; + int m, s, f; + + burn_lba_to_msf(lba, &m, &s, &f); + + sheet->count++; + ptr = realloc(sheet->data, sheet->count * 8); + assert(ptr); + sheet->data = ptr; + unit = sheet->data + (sheet->count - 1) * 8; + unit[0] = ctladr; + unit[1] = tno; + unit[2] = indx; + unit[3] = form; + unit[4] = scms; + unit[5] = m; + unit[6] = s; + unit[7] = f; +} + +struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o, + struct burn_session *session) +{ + int i, m, s, f, form, pform, runtime = -150; + unsigned char ctladr; + struct burn_drive *d; + struct burn_toc_entry *e; + struct cue_sheet *sheet; + struct burn_track **tar = session->track; + int ntr = session->tracks; + int rem = 0; + + d = o->drive; + + sheet = malloc(sizeof(struct cue_sheet)); + sheet->data = NULL; + sheet->count = 0; + + type_to_form(tar[0]->mode, &ctladr, &form); + add_cue(sheet, ctladr | 1, 0, 0, 1, 0, runtime); + add_cue(sheet, ctladr | 1, 1, 0, form, 0, runtime); + runtime += 150; + + burn_print(1, "toc for %d tracks:\n", ntr); + d->toc_entries = ntr + 3; + assert(d->toc_entry == NULL); + d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry)); + e = d->toc_entry; + memset((void *)e, 0, d->toc_entries * sizeof(struct burn_toc_entry)); + e[0].point = 0xA0; + if (tar[0]->mode & BURN_AUDIO) + e[0].control = TOC_CONTROL_AUDIO; + else + e[0].control = TOC_CONTROL_DATA; + e[0].pmin = 1; + e[0].psec = o->format; + e[0].adr = 1; + e[1].point = 0xA1; + e[1].pmin = ntr; + e[1].adr = 1; + if (tar[ntr - 1]->mode & BURN_AUDIO) + e[1].control = TOC_CONTROL_AUDIO; + else + e[1].control = TOC_CONTROL_DATA; + e[2].point = 0xA2; + e[2].control = e[1].control; + e[2].adr = 1; + + tar[0]->pregap2 = 1; + pform = form; + for (i = 0; i < ntr; i++) { + type_to_form(tar[i]->mode, &ctladr, &form); + if (pform != form) { + add_cue(sheet, ctladr | 1, i + 1, 0, form, 0, runtime); + runtime += 150; +/* XXX fix pregap interval 1 for data tracks */ +// if (!(form & BURN_AUDIO)) +// tar[i]->pregap1 = 1; + tar[i]->pregap2 = 1; + } +/* XXX HERE IS WHERE WE DO INDICES IN THE CUE SHEET */ +/* XXX and we should make sure the gaps conform to ecma-130... */ + tar[i]->entry = &e[3 + i]; + e[3 + i].point = i + 1; + burn_lba_to_msf(runtime, &m, &s, &f); + e[3 + i].pmin = m; + e[3 + i].psec = s; + e[3 + i].pframe = f; + e[3 + i].adr = 1; + e[3 + i].control = type_to_ctrl(tar[i]->mode); + burn_print(1, "track %d control %d\n", tar[i]->mode, + e[3 + i].control); + add_cue(sheet, ctladr | 1, i + 1, 1, form, 0, runtime); + runtime += burn_track_get_sectors(tar[i]); +/* if we're padding, we'll clear any current shortage. + if we're not, we'll slip toc entries by a sector every time our + shortage is more than a sector +XXX this is untested :) +*/ + if (!tar[i]->pad) { + rem += burn_track_get_shortage(tar[i]); + if (i +1 != ntr) + tar[i]->source->next = tar[i+1]->source; + } else if (rem) { + rem = 0; + runtime++; + } + if (rem > burn_sector_length(tar[i]->mode)) { + rem -= burn_sector_length(tar[i]->mode); + runtime--; + } + pform = form; + } + burn_lba_to_msf(runtime, &m, &s, &f); + e[2].pmin = m; + e[2].psec = s; + e[2].pframe = f; + burn_print(1, "run time is %d (%d:%d:%d)\n", runtime, m, s, f); + for (i = 0; i < d->toc_entries; i++) + burn_print(1, "point %d (%02d:%02d:%02d)\n", + d->toc_entry[i].point, d->toc_entry[i].pmin, + d->toc_entry[i].psec, d->toc_entry[i].pframe); + add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime); + return sheet; +} + +int burn_sector_length(int tracktype) +{ + if (tracktype & BURN_AUDIO) + return 2352; + if (tracktype & BURN_MODE_RAW) + return 2352; + if (tracktype & BURN_MODE1) + return 2048; + assert(0); + return 12345; +} + +int burn_subcode_length(int tracktype) +{ + if (tracktype & BURN_SUBCODE_P16) + return 16; + if ((tracktype & BURN_SUBCODE_P96) || (tracktype & BURN_SUBCODE_R96)) + return 96; + return 0; +} + +int burn_write_leadin(struct burn_write_opts *o, + struct burn_session *s, int first) +{ + struct burn_drive *d = o->drive; + int count; + + d->busy = BURN_DRIVE_WRITING_LEADIN; + + burn_print(5, first ? " first leadin\n" : " leadin\n"); + + if (first) + count = 0 - d->alba - 150; + else + count = 4500; + + d->progress.start_sector = d->alba; + d->progress.sectors = count; + d->progress.sector = 0; + + while (count != 0) { + if (!sector_toc(o, s->track[0]->mode)) + return 0; + count--; + d->progress.sector++; + } + d->busy = BURN_DRIVE_WRITING; + return 1; +} + +int burn_write_leadout(struct burn_write_opts *o, + int first, unsigned char control, int mode) +{ + struct burn_drive *d = o->drive; + int count; + + d->busy = BURN_DRIVE_WRITING_LEADOUT; + + d->rlba = -150; + burn_print(5, first ? " first leadout\n" : " leadout\n"); + if (first) + count = 6750; + else + count = 2250; + d->progress.start_sector = d->alba; + d->progress.sectors = count; + d->progress.sector = 0; + + while (count != 0) { + if (!sector_lout(o, control, mode)) + return 0; + count--; + d->progress.sector++; + } + d->busy = BURN_DRIVE_WRITING; + return 1; +} + +int burn_write_session(struct burn_write_opts *o, struct burn_session *s) +{ + struct burn_drive *d = o->drive; + struct burn_track *prev = NULL, *next = NULL; + int i; + + d->rlba = 0; + burn_print(1, " writing a session\n"); + for (i = 0; i < s->tracks; i++) { + if (i > 0) + prev = s->track[i - 1]; + if (i + 1 < s->tracks) + next = s->track[i + 1]; + else + next = NULL; + + if (!burn_write_track(o, s, i)) + return 0; + } + return 1; +} + +int burn_write_track(struct burn_write_opts *o, struct burn_session *s, + int tnum) +{ + struct burn_track *t = s->track[tnum]; + struct burn_drive *d = o->drive; + int i, tmp = 0; + int sectors; + + d->rlba = -150; + +/* XXX for tao, we don't want the pregaps but still want post? */ + if (o->write_type != BURN_WRITE_TAO) { + if (t->pregap1) + d->rlba += 75; + if (t->pregap2) + d->rlba += 150; + + if (t->pregap1) { + struct burn_track *pt = s->track[tnum - 1]; + + if (tnum == 0) { + printf("first track should not have a pregap1\n"); + pt = t; + } + for (i = 0; i < 75; i++) + if (!sector_pregap(o, t->entry->point, + pt->entry->control, pt->mode)) + return 0; + } + if (t->pregap2) + for (i = 0; i < 150; i++) + if (!sector_pregap(o, t->entry->point, + t->entry->control, t->mode)) + return 0; + } else { + o->control = t->entry->control; + d->send_write_parameters(d, o); + } + +/* user data */ + sectors = burn_track_get_sectors(t); + + /* Update progress */ + d->progress.start_sector = d->nwa; + d->progress.sectors = sectors; + d->progress.sector = 0; + + burn_print(12, "track is %d sectors long\n", sectors); + + if (tnum == s->tracks) + tmp = sectors > 150 ? 150 : sectors; + + for (i = 0; i < sectors - tmp; i++) { + if (!sector_data(o, t, 0)) + return 0; + + /* update current progress */ + d->progress.sector++; + } + for (; i < sectors; i++) { + burn_print(1, "last track, leadout prep\n"); + if (!sector_data(o, t, 1)) + return 0; + + /* update progress */ + d->progress.sector++; + } + + if (t->postgap) + for (i = 0; i < 150; i++) + if (!sector_postgap(o, t->entry->point, t->entry->control, + t->mode)) + return 0; + i = t->offset; + if (o->write_type == BURN_WRITE_SAO) { + if (d->buffer->bytes) { + int err; + err = d->write(d, d->nwa, d->buffer); + if (err == BE_CANCELLED) + return 0; + d->nwa += d->buffer->sectors; + d->buffer->bytes = 0; + d->buffer->sectors = 0; + } + } + if (o->write_type == BURN_WRITE_TAO) + if (!burn_write_flush(o)) + return 0; + return 1; +} + +void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc) +{ + struct cue_sheet *sheet; + struct burn_drive *d = o->drive; + struct buffer buf; + struct burn_track *lt; + int first = 1, i; + int res; + + burn_message_clear_queue(); + + burn_print(1, "sync write of %d sessions\n", disc->sessions); + d->buffer = &buf; + memset(d->buffer, 0, sizeof(struct buffer)); + + d->rlba = -150; + + d->toc_temp = 9; + +/* Apparently some drives require this command to be sent, and a few drives +return crap. so we send the command, then ignore the result. +*/ + res = d->get_nwa(d); +/* printf("ignored nwa: %d\n", res);*/ + + d->alba = d->start_lba; + d->nwa = d->alba; + + if (o->write_type != BURN_WRITE_TAO) + d->send_write_parameters(d, o); + + /* init progress before showing the state */ + d->progress.session = 0; + d->progress.sessions = disc->sessions; + d->progress.track = 0; + d->progress.tracks = disc->session[0]->tracks; + /* TODO: handle indices */ + d->progress.index = 0; + d->progress.indices = disc->session[0]->track[0]->indices; + /* TODO: handle multissession discs */ + /* XXX: sectors are only set during write track */ + d->progress.start_sector = 0; + d->progress.sectors = 0; + d->progress.sector = 0; + + d->busy = BURN_DRIVE_WRITING; + + for (i = 0; i < disc->sessions; i++) { + /* update progress */ + d->progress.session = i; + d->progress.tracks = disc->session[i]->tracks; + + sheet = burn_create_toc_entries(o, disc->session[i]); +/* print_cue(sheet);*/ + if (o->write_type == BURN_WRITE_SAO) + d->send_cue_sheet(d, sheet); + free(sheet); + + if (o->write_type == BURN_WRITE_RAW) { + if (!burn_write_leadin(o, disc->session[i], first)) + goto fail; + } else { + if (first) { + d->nwa = -150; + d->alba = -150; + } else { + d->nwa += 4500; + d->alba += 4500; + } + } + if (!burn_write_session(o, disc->session[i])) + goto fail; + + lt = disc->session[i]->track[disc->session[i]->tracks - 1]; + if (o->write_type == BURN_WRITE_RAW) { + if (!burn_write_leadout(o, first, lt->entry->control, + lt->mode)) + goto fail; + } else { + if (!burn_write_flush(o)) + goto fail; + d->nwa += first ? 6750 : 2250; + d->alba += first ? 6750 : 2250; + } + if (first) + first = 0; + + /* XXX: currently signs an end of session */ + d->progress.sector = 0; + d->progress.start_sector = 0; + d->progress.sectors = 0; + } + if (o->write_type != BURN_WRITE_SAO) + if (!burn_write_flush(o)) + goto fail; + sleep(1); + + burn_print(1, "done\n"); + d->busy = BURN_DRIVE_IDLE; + +fail: + d->sync_cache(d); + burn_print(1, "done - failed\n"); + d->busy = BURN_DRIVE_IDLE; +} diff --git a/libburn/write.h b/libburn/write.h new file mode 100644 index 0000000..d7eb733 --- /dev/null +++ b/libburn/write.h @@ -0,0 +1,25 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef BURN__WRITE_H +#define BURN__WRITE_H + +struct cue_sheet; +struct burn_session; +struct burn_write_opts; +struct burn_disc; + +struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o, + struct burn_session *session); +int burn_sector_length(int trackmode); +int burn_subcode_length(int trackmode); +void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc); +int burn_write_leadin(struct burn_write_opts *o, + struct burn_session *s, int first); +int burn_write_leadout(struct burn_write_opts *o, + int first, unsigned char control, int mode); +int burn_write_session(struct burn_write_opts *o, struct burn_session *s); +int burn_write_track(struct burn_write_opts *o, struct burn_session *s, + int tnum); +int burn_write_flush(struct burn_write_opts *o); + +#endif /* BURN__WRITE_H */ diff --git a/libisofs-1.pc.in b/libisofs-1.pc.in new file mode 100644 index 0000000..fb21b56 --- /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}/libburn/@BURN_MAJOR_VERSION@ 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/Makefile.am b/libisofs/Makefile.am new file mode 100755 index 0000000..042bc7e --- /dev/null +++ b/libisofs/Makefile.am @@ -0,0 +1,44 @@ +pkgconfigdir=$(libdir)/pkgconfig +libincludedir=$(includedir)/libburn/@BURN_MAJOR_VERSION@ + +lib_LTLIBRARIES = libisofs.la + +libisofs_la_SOURCES = \ + errors.h \ + errors.c \ + tree.h \ + tree.c \ + volume.h \ + volume.c \ + util.h \ + util.c \ + ecma119.c \ + ecma119.h \ + struct.h \ + struct.c \ + susp.h \ + susp.c \ + rockridge.h \ + rockridge.c + +libinclude_HEADERS = libisofs.h + +noinst_PROGRAMS = test +test_SOURCES = test.c +test_LDADD = $(libisofs_la_OBJECTS) + +INCLUDES = -I.. + +## ========================================================================= ## +indent_files = $(libisofs_la_SOURCES) + +indent: $(indent_files) + indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \ + -cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \ + -lp -saf -sai -nprs -npsl -saw -sob -ss -ut \ + -sbi0 -nsc -ts8 -npcs -ncdb -fca \ + $^ + +.PHONY: indent + +## ========================================================================= ## diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c new file mode 100755 index 0000000..6df65eb --- /dev/null +++ b/libisofs/ecma119.c @@ -0,0 +1,1506 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "tree.h" +#include "ecma119.h" +#include "volume.h" +#include "util.h" +#include "struct.h" +#include "rockridge.h" +#include "libisofs.h" +#include "libburn/libburn.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char* const ecma119_standard_id = "CD001"; + +/* Format definitions */ +const char* const ecma119_vol_fmt = "B5bB"; /* common between all vol descs */ + +const char* const ecma119_privol_fmt = + "B" /* volume desc type */ + "5b" /* standard id */ + "B" /* volume desc version */ + "x" + "32b" /* system id */ + "32b" /* volume id */ + "8x" + "=L" /* volume space size */ + "32x" + "=H" /* volume set size */ + "=H" /* volume sequence number */ + "=H" /* block size */ + "=L" /* path table size */ + "L" /* m path table 1 */ + ">L" /* m path table 2 */ + "34B" /* root directory record */ + "128b" /* volume id */ + "128b" /* publisher id */ + "128b" /* data preparer id */ + "128b" /* application id */ + "37b" /* copyright file id */ + "37b" /* abstract file id */ + "37b" /* bibliographic file id */ + "T" /* creation timestamp */ + "T" /* modification timestamp */ + "T" /* expiration timestamp */ + "T" /* effective timestamp */ + "B" /* file structure version */ + "x" + "512x" /* Application Use */ + "653x"; /* reserved */ + +const char* const ecma119_supvol_joliet_fmt = + "B" /* volume desc type */ + "5b" /* standard id */ + "B" /* volume desc version */ + "B" /* volume flags */ + "16h" /* system id */ + "16h" /* volume id */ + "8x" + "=L" /* volume space size */ + "32b" /* escape sequences */ + "=H" /* volume set size */ + "=H" /* volume sequence number */ + "=H" /* block size */ + "=L" /* path table size */ + "L" /* m path table 1 */ + ">L" /* m path table 2 */ + "34B" /* root directory record */ + "64h" /* volume id */ + "64h" /* publisher id */ + "64h" /* data preparer id */ + "64h" /* application id */ + "18hx" /* copyright file id */ + "18hx" /* abstract file id */ + "18hx" /* bibliographic file id */ + "T" /* creation timestamp */ + "T" /* modification timestamp */ + "T" /* expiration timestamp */ + "T" /* effective timestamp */ + "B" /* file structure version */ + "x" + "512x" /* Application Use */ + "653x"; /* reserved */ + +const char* const ecma119_dir_record_fmt = + "B" /* dir record length */ + "B" /* extended attribute length */ + "=L" /* block */ + "=L" /* size */ + "S" + "B" /* file flags */ + "B" /* file unit size */ + "B" /* interleave gap size */ + "=H" /* volume sequence number */ + "B"; /* name length */ + /* file id ansa SU field have variable length, so they don't get defined + * yet. */ + +/* abstract string functions away from char* to make Joliet stuff easier */ +typedef void (*copy_string)(unsigned char *dest, void *str, int maxlen); +typedef int (*node_namelen)(struct iso_tree_node *node); +typedef const void* (*node_getname)(struct iso_tree_node *node); + +/* because we don't write SUSP fields in the Joliet Directory Records, it helps + * to abstract away the task of getting the non_CE_len of a susp_info. + */ +typedef int (*susp_len)(struct susp_info*); +typedef int (*total_dirent_len)(struct iso_tree_node*); + +const char application_id[] = "LIBBURN SUITE (C) 2002 D.FOREMAN/B.JANSENS"; +const char system_id[] = "LINUX"; + +const uint16_t* application_id_joliet = (uint16_t*) "\0j\0a\0p\0p\0i\0d\0\0"; +const uint16_t* system_id_joliet = (uint16_t*) "\0j\0s\0y\0s\0i\0d\0\0"; + +/* since we have to pass things by address to iso_struct_pack, this makes + * it easier. */ +const uint8_t zero = 0; +const uint8_t one = 1; + +enum RecordType { + RECORD_TYPE_SELF, + RECORD_TYPE_PARENT, + RECORD_TYPE_NORMAL, + RECORD_TYPE_ROOT +}; + +/* layout functions */ +static void ecma119_reorganize_heirarchy(struct ecma119_write_target *target, + struct iso_tree_dir *dir); +static void ecma119_alloc_writer_data(struct ecma119_write_target *target, + struct iso_tree_dir *dir); +static void ecma119_setup_path_tables_iso(struct ecma119_write_target*); +static void ecma119_setup_path_tables_joliet(struct ecma119_write_target*); + +/* burn_source functions */ +static int ecma119_read(struct burn_source*, unsigned char*, int); +static int ecma119_get_size(struct burn_source*); +static void ecma119_free_data(struct burn_source*); + +/* Writers for the different write_states. */ +static void ecma119_write_system_area(struct ecma119_write_target*, + unsigned char *buf); +static void ecma119_write_privol_desc(struct ecma119_write_target*, + unsigned char *buf); +static void ecma119_write_supvol_desc_joliet(struct ecma119_write_target*, + unsigned char *buf); +static void ecma119_write_vol_desc_terminator(struct ecma119_write_target*, + unsigned char *buf); +static void ecma119_write_path_table(struct ecma119_write_target*, + unsigned char *buf, + int flags, + int m_type); +static void ecma119_write_dir_records(struct ecma119_write_target*, + unsigned char *buf, + int flags); +static void ecma119_write_files(struct ecma119_write_target*, + unsigned char *buf); + +/* helpers for the writers */ +static void ecma119_write_path_table_full(struct ecma119_write_target*, + unsigned char *buf, + int flags, + int mtype); +static unsigned char *ecma119_write_dir(struct ecma119_write_target*, + int flags, + struct iso_tree_dir*); +static void ecma119_write_dir_record(struct ecma119_write_target*, + unsigned char *, + int flags, + struct iso_tree_node*, + enum RecordType); +static void ecma119_write_dir_record_iso(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type); +static void ecma119_write_dir_record_joliet(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type); +static void ecma119_write_dir_record_noname(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type, + uint32_t block, + uint32_t size, + int joliet); + + +/* name-mangling routines */ + +/* return a newly allocated array of pointers to all the children, in + * in alphabetical order and with mangled names. */ +static struct iso_tree_node **ecma119_mangle_names(struct iso_tree_dir *dir); + +/* mangle a single filename according to where the last '.' is: + * \param name the name to mangle + * \param num_change the number of characters to mangle + * \param seq_num the string ("%0d", seq_num) to which it should + * be mangled + */ +static void ecma119_mangle_name(char *name, + int num_change, + int seq_num); + +/* mangle a single filename based on the explicit positions passed. That is, + * the difference between this and ecma119_mangle_name_iso is that this one + * doesn't look for the '.' character; it changes the character at the + * beginning of name. */ +static void ecma119_mangle_name_priv(char *name, + int num_change, + int seq_num); + +/* return a newly allocated array of pointers to all the children, in + * alphabetical order. The difference between this and ecma119_mangle_names + * is that this function sorts the files according to joliet names and it + * doesn't mangle the names. */ +static struct iso_tree_node **ecma119_sort_joliet(struct iso_tree_dir *dir); + +/* string abstraction functions */ +static int node_namelen_iso(struct iso_tree_node *node) +{ + return strlen(iso_tree_node_get_name(node, ISO_NAME_ISO)); +} + +static int node_namelen_joliet(struct iso_tree_node *node) +{ + const char *name = iso_tree_node_get_name(node, ISO_NAME_JOLIET); + + /* return length in bytes, not length in characters */ + return ucslen((uint16_t*)name) * 2; +} + +static const void *node_getname_iso(struct iso_tree_node *node) +{ + return iso_tree_node_get_name(node, ISO_NAME_ISO); +} + +static const void *node_getname_joliet(struct iso_tree_node *node) +{ + return iso_tree_node_get_name(node, ISO_NAME_JOLIET); +} + +static int susp_len_iso(struct susp_info *s) +{ + return s->non_CE_len; +} + +static int susp_len_joliet(struct susp_info *s) +{ + return 0; +} + +static int dirent_len_iso(struct iso_tree_node *n) +{ + return n->dirent_len + GET_NODE_INF(n)->susp.non_CE_len; +} + +static int dirent_len_joliet(struct iso_tree_node *n) +{ + return 34 + node_namelen_joliet(n); +} + +static void print_dir_info(struct iso_tree_dir *dir, + struct ecma119_write_target *t, + int spaces) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + for (i=0; inon_CE_len=%d, " + "parent->non_CE_len=%d, len=%d, blk=%d, jblk=%d\n", + inf->susp.non_CE_len, + inf->susp.CE_len, + inf->self_susp.non_CE_len, + inf->parent_susp.non_CE_len, + inf->len, + (int)dir->block, + (int)inf->joliet_block); +} + +static void print_file_info(struct iso_tree_file *file, + struct ecma119_write_target *t, + int spaces) +{ + struct file_write_info *inf = GET_FILE_INF(file); + int i; + + for (i=0; idirent_len, + inf->susp.non_CE_len, + inf->susp.CE_len, + (int)file->attrib.st_size, + (int)file->block); +} + +static struct iso_tree_node **ecma119_mangle_names(struct iso_tree_dir *dir) +{ + size_t retsize = dir->nfiles + dir->nchildren; + struct iso_tree_node **ret = calloc(1, sizeof(void*) * retsize); + int i, j, k; + + j = 0; + for (i=0; infiles; i++) { + ret[j++] = ISO_NODE(dir->files[i]); + } + for (i=0; inchildren; i++) { + ret[j++] = ISO_NODE(dir->children[i]); + } + + qsort(ret, retsize, sizeof(void*), iso_node_cmp_iso); + + for (i=0; i= 10) { + return; + } + + overwritten = name[num_change]; + sprintf(fmt, "%%0%1dd", num_change); + sprintf(name, fmt, seq_num); + name[num_change] = overwritten; +} + +static struct iso_tree_node **ecma119_sort_joliet(struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + size_t retsize = dir->nfiles + inf->real_nchildren; + struct iso_tree_node **ret = malloc(retsize * sizeof(void*)); + int i, j; + + j = 0; + for (i=0; infiles; i++) { + ret[j++] = ISO_NODE(dir->files[i]); + } + for (i=0; ireal_nchildren; i++) { + ret[j++] = ISO_NODE(inf->real_children[i]); + } + qsort(ret, retsize, sizeof(void*), iso_node_cmp_joliet); + + return ret; +} + +struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset, + int volnum) +{ + struct ecma119_write_target *t; + + assert(volnum < volset->volset_size); + + t = calloc(1, sizeof(struct ecma119_write_target)); + t->volset = volset; + t->volnum = volnum; + t->now = time(NULL); + t->block_size = 2048; + + iso_tree_sort(TARGET_ROOT(t)); + ecma119_alloc_writer_data(t, TARGET_ROOT(t)); + ecma119_reorganize_heirarchy(t, TARGET_ROOT(t)); + + return t; +} + +/** + * Write create all necessary SUSP fields for the given directory and + * initialise them. Any SUSP fields that require an offset won't be completed + * yet. Ensure that the size fields in the susp_info structs are correct. + */ +static void ecma119_susp_dir_layout(struct ecma119_write_target *target, + struct iso_tree_dir *dir, + int flags) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + struct susp_info *susp = &inf->susp; + susp->n_susp_fields = 0; + susp->susp_fields = NULL; + + if (!target->rockridge) { + /* since Rock Ridge is the only SUSP extension supported, + * no need to continue. */ + return; + } + + if (dir->depth == 1) { + susp_add_SP(target, dir); + susp_add_ER(target, dir); + } else { + rrip_add_NM(target, ISO_NODE(dir)); + rrip_add_TF(target, ISO_NODE(dir)); + } + rrip_add_PX_dir(target, dir); + + if (inf->real_parent != dir->parent) { + rrip_add_RE(target, ISO_NODE(dir)); + rrip_add_PL(target, dir); + } + susp_add_CE(target, ISO_NODE(dir)); +} + +/** + * Write create all necessary SUSP fields for the given file and + * initialise them. Any SUSP fields that require an offset won't be completed + * yet. Ensure that the size fields in the susp_info structs are correct. + */ +static void ecma119_susp_file_layout(struct ecma119_write_target *target, + struct iso_tree_file *file) +{ + struct file_write_info *inf = GET_FILE_INF(file); + + if (!target->rockridge) { + return; + } + + rrip_add_PX(target, ISO_NODE(file)); + rrip_add_NM(target, ISO_NODE(file)); + rrip_add_TF(target, ISO_NODE(file)); + + if (inf->real_me) { + rrip_add_CL(target, ISO_NODE(file)); + } + if (S_ISLNK(file->attrib.st_mode)) { + rrip_add_SL(target, ISO_NODE(file)); + } + if (S_ISCHR(file->attrib.st_mode) || S_ISBLK(file->attrib.st_mode)) { + rrip_add_PN(target, ISO_NODE(file)); + } + susp_add_CE(target, ISO_NODE(file)); +} + +/** + * Recursively allocate the writer_data pointer for each node in the tree. + * Also, save the current state of the tree in the inf->real_XXX pointers. + */ +static void ecma119_alloc_writer_data(struct ecma119_write_target *target, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf; + int i; + + inf = calloc(1, sizeof(struct dir_write_info)); + inf->real_parent = dir->parent; + inf->real_nchildren = dir->nchildren; + inf->real_children = malloc(sizeof(void*) * dir->nchildren); + memcpy(inf->real_children, dir->children, dir->nchildren*sizeof(void*)); + inf->real_depth = dir->depth; + + dir->writer_data = inf; + + for (i=0; inchildren; i++) { + ecma119_alloc_writer_data(target, dir->children[i]); + } + for (i=0; infiles; i++) { + dir->files[i]->writer_data = + calloc(1, sizeof(struct file_write_info)); + } +} + +/* ensure that the maximum height of the directory tree is 8, repositioning + * directories as necessary */ +static void ecma119_reorganize_heirarchy(struct ecma119_write_target *target, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + struct iso_tree_dir *root = TARGET_ROOT(target); + struct iso_tree_file *file; + struct file_write_info *finf; + + /* save this now in case a recursive call modifies this value */ + int nchildren = dir->nchildren; + int i; + + if (dir == root) { + dir->depth = 1; + } else { + dir->depth = dir->parent->depth + 1; + + assert(dir->depth <= 9); + if (dir->depth == 9) { + dir->depth = 2; + dir->parent = root; + root->nchildren++; + root->children = realloc(root->children, + root->nchildren * sizeof(void*)); + root->children[root->nchildren-1] = dir; + + /* no need to reshuffle the siblings since we know that + * _every_ sibling will also be relocated. */ + inf->real_parent->nchildren--; + if (!inf->real_parent->nchildren) { + free(inf->real_parent->children); + inf->real_parent->children = NULL; + } + /* insert a placeholder file for the CL field */ + file = iso_tree_add_new_file(inf->real_parent, + dir->name.full); + finf = calloc(1, sizeof(struct file_write_info)); + finf->real_me = dir; + file->writer_data = finf; + } + } + for (i=0; ichildren[i]); + } +} + +/* set directory sizes recursively. Also fill out the dirlist_len and + * filelist_len fields in the ecma119 writer. + */ +static void ecma119_target_rsize(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + int i; + struct dir_write_info *inf = GET_DIR_INF(dir); + struct node_write_info *cinf; + + t->dirlist_len++; + + /* work out the size of the dirents */ + if (dir->depth == 1) { + dir->dirent_len = 34; + } else { + dir->dirent_len = 33 + node_namelen_iso(ISO_NODE(dir)); + } + dir->dirent_len += dir->dirent_len % 2; + + for (i=0; infiles; i++) { + dir->files[i]->dirent_len = 33 + node_namelen_iso( + ISO_NODE(dir->files[i])); + dir->files[i]->dirent_len += dir->files[i]->dirent_len % 2; + + if (dir->files[i]->path && dir->files[i]->attrib.st_size) { + t->filelist_len++; + } + } + + /* layout all the susp entries and calculate the total size */ + ecma119_susp_dir_layout(t, dir, 0); + + inf->len = 34 + inf->self_susp.non_CE_len /* for "." and ".." */ + + 34 + inf->parent_susp.non_CE_len; + inf->susp_len = inf->susp.CE_len + + inf->self_susp.CE_len + + inf->parent_susp.CE_len; + + for (i=0; infiles; i++) { + ecma119_susp_file_layout(t, dir->files[i]); + cinf = GET_NODE_INF(dir->files[i]); + inf->len += dir->files[i]->dirent_len + cinf->susp.non_CE_len; + inf->susp_len += cinf->susp.CE_len; + } + for (i=0; inchildren; i++) { + ecma119_target_rsize(t, dir->children[i]); + cinf = GET_NODE_INF(dir->children[i]); + inf->len += dir->children[i]->dirent_len +cinf->susp.non_CE_len; + inf->susp_len += cinf->susp.CE_len; + } + dir->attrib.st_size = inf->len; /* the actual size of the data is + * inf->len + inf->susp_len because we + * append the CE data to the end of the + * directory. But the ISO volume + * doesn't need to know. + */ +} + +static void ecma119_target_rsize_joliet(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + inf->joliet_len = 34 + 34; /* for "." and ".." */ + for (i=0; infiles; i++) { + /* don't count files that are placeholders for Rock Ridge + * relocated directories */ + if (!GET_FILE_INF(dir->files[i])->real_me) { + inf->joliet_len += + dirent_len_joliet(ISO_NODE(dir->files[i])); + } + } + for (i=0; ireal_nchildren; i++) { + struct iso_tree_node *ch = ISO_NODE(inf->real_children[i]); + inf->joliet_len += dirent_len_joliet(ch); + ecma119_target_rsize_joliet(t, inf->real_children[i]); + } +} + +/* set directory positions recursively. Also fill out the dirlist in the + * ecma119_write_target */ +static void ecma119_target_dir_layout(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + t->dirlist[t->curfile++] = dir; + dir->block = t->curblock; + t->curblock += DIV_UP(inf->len + inf->susp_len, 2048); + + for (i=0; inchildren; i++) { + ecma119_target_dir_layout(t, dir->children[i]); + } +} + +/* same as ecma119_target_dir_layout, but for Joliet. */ +static void ecma119_target_dir_layout_joliet(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + t->dirlist_joliet[t->curfile++] = dir; + inf->joliet_block = t->curblock; + t->curblock += DIV_UP(inf->joliet_len, 2048); + + for (i=0; ireal_nchildren; i++) { + ecma119_target_dir_layout_joliet(t, inf->real_children[i]); + } +} + +/* set file positions recursively. Also fill in the filelist in the + * ecma119_write_target */ +static void ecma119_target_file_layout(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + int i; + + for (i=0; infiles; i++) { + if (dir->files[i]->path && dir->files[i]->attrib.st_size) { + t->filelist[t->curfile++] = dir->files[i]; + } + dir->files[i]->block = t->curblock; + t->curblock += DIV_UP(dir->files[i]->attrib.st_size, 2048); + } + for (i=0; inchildren; i++) { + ecma119_target_file_layout(t, dir->children[i]); + } +} + +void ecma119_target_layout(struct ecma119_write_target *t) +{ + ecma119_target_rsize(t, TARGET_ROOT(t)); + if (t->joliet) { + ecma119_target_rsize_joliet(t, TARGET_ROOT(t)); + } + ecma119_setup_path_tables_iso(t); + t->curblock = 16 /* for the system area */ + + 1 /* volume desc */ + + 1; /* volume desc terminator */ + if (t->joliet) { + t->curblock++; /* joliet supplementary volume desc */ + } + + t->l_path_table_pos = t->curblock; + t->curblock += DIV_UP(t->path_table_size, 2048); + t->m_path_table_pos = t->curblock; + t->curblock += DIV_UP(t->path_table_size, 2048); + + if (t->joliet) { + ecma119_setup_path_tables_joliet(t); + t->l_path_table_pos_joliet = t->curblock; + t->curblock += DIV_UP(t->path_table_size_joliet, 2048); + t->m_path_table_pos_joliet = t->curblock; + t->curblock += DIV_UP(t->path_table_size_joliet, 2048); + } + + t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len); + t->filelist = calloc(1, sizeof(void*) * t->filelist_len); + t->curfile = 0; + ecma119_target_dir_layout(t, TARGET_ROOT(t)); + + if (t->joliet) { + t->curfile = 0; + t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len); + ecma119_target_dir_layout_joliet(t, TARGET_ROOT(t)); + } + + t->curfile = 0; + ecma119_target_file_layout(t, TARGET_ROOT(t)); + t->total_size = t->curblock * 2048; + t->vol_space_size = t->curblock; + + if (t->rockridge) { + susp_finalize(t, TARGET_ROOT(t)); + rrip_finalize(t, TARGET_ROOT(t)); + } + + iso_tree_print_verbose(TARGET_ROOT(t), + (print_dir_callback)print_dir_info, + (print_file_callback)print_file_info, + t, 0); + + /* prepare for writing */ + t->curblock = -1; + t->state = ECMA119_WRITE_SYSTEM_AREA; +} + +struct burn_source *iso_source_new_ecma119(struct iso_volset *volset, + int volnum, + int level, + int flags) +{ + struct burn_source *src = calloc(1, sizeof(struct burn_source)); + struct ecma119_write_target *t = ecma119_target_new(volset, volnum); + struct iso_volume *vol = volset->volume[volnum]; + + t->iso_level = level; + t->rockridge = (flags & ECMA119_ROCKRIDGE) ? 1:0; + t->joliet = (flags & ECMA119_JOLIET) ? 1:0; + + vol->iso_level = t->iso_level; + vol->rockridge = t->rockridge; + vol->joliet = t->joliet; + + ecma119_target_layout(t); + src->read = ecma119_read; + src->read_sub = NULL; + src->get_size = ecma119_get_size; + src->free_data = ecma119_free_data; + src->data = t; + return src; +} + +static int ecma119_read(struct burn_source *src, + unsigned char *data, + int size) +{ + struct ecma119_write_target *t = src->data; + + assert( src->read == ecma119_read && src->get_size == ecma119_get_size + && src->free_data == ecma119_free_data + && size == 2048); + + t->curblock++; + memset(data, 0, size); + switch(t->state) { + case ECMA119_WRITE_SYSTEM_AREA: + ecma119_write_system_area(t, data); + break; + case ECMA119_WRITE_PRI_VOL_DESC: + ecma119_write_privol_desc(t, data); + break; + case ECMA119_WRITE_SUP_VOL_DESC_JOLIET: + ecma119_write_supvol_desc_joliet(t, data); + break; + case ECMA119_WRITE_VOL_DESC_TERMINATOR: + ecma119_write_vol_desc_terminator(t, data); + break; + case ECMA119_WRITE_L_PATH_TABLE: + ecma119_write_path_table(t, data, 0, 0); + break; + case ECMA119_WRITE_M_PATH_TABLE: + ecma119_write_path_table(t, data, 0, 1); + break; + case ECMA119_WRITE_L_PATH_TABLE_JOLIET: + ecma119_write_path_table(t, data, ECMA119_JOLIET, 0); + break; + case ECMA119_WRITE_M_PATH_TABLE_JOLIET: + ecma119_write_path_table(t, data, ECMA119_JOLIET, 1); + break; + case ECMA119_WRITE_DIR_RECORDS: + ecma119_write_dir_records(t, data, 0); + break; + case ECMA119_WRITE_DIR_RECORDS_JOLIET: + ecma119_write_dir_records(t, data, ECMA119_JOLIET); + break; + case ECMA119_WRITE_FILES: + ecma119_write_files(t, data); + break; + case ECMA119_WRITE_DONE: + return 0; + default: + assert(0); + } + + return 2048; +} + +static int ecma119_get_size(struct burn_source *src) +{ + struct ecma119_write_target *t = src->data; + + assert( src->read == ecma119_read && src->get_size == ecma119_get_size + && src->free_data == ecma119_free_data); + + return t->total_size; +} + +/* free writer_data fields recursively */ +static void ecma119_free_writer_data(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int i; + + susp_free_fields(&inf->susp); + susp_free_fields(&inf->self_susp); + susp_free_fields(&inf->parent_susp); + + if (inf->real_children) { + free(inf->real_children); + } + + for (i=0; infiles; i++) { + struct file_write_info *finf = GET_FILE_INF(dir->files[i]); + susp_free_fields(&finf->susp); + } + for (i=0; inchildren; i++) { + ecma119_free_writer_data(t, dir->children[i]); + } +} + +static void ecma119_free_data(struct burn_source *src) +{ + struct ecma119_write_target *t = src->data; + + assert( src->read == ecma119_read && src->get_size == ecma119_get_size + && src->free_data == ecma119_free_data); + + if (t->filelist) free(t->filelist); + if (t->dirlist) free(t->dirlist); + if (t->pathlist) free(t->pathlist); + if (t->dirlist_joliet) free(t->dirlist_joliet); + if (t->pathlist_joliet) free(t->pathlist_joliet); + + ecma119_free_writer_data(t, TARGET_ROOT(t)); + + free(t); + src->data = NULL; + src->read = NULL; + src->get_size = NULL; + src->free_data = NULL; +} + +/*============================================================================*/ +/* Writing functions */ +/*============================================================================*/ + +static void ecma119_write_system_area(struct ecma119_write_target *t, + unsigned char *buf) +{ + if (t->curblock == 15) { + t->state = ECMA119_WRITE_PRI_VOL_DESC; + } +} + +static void ecma119_write_privol_desc(struct ecma119_write_target *t, + unsigned char *buf) +{ + struct iso_tree_node *root = ISO_NODE(TARGET_ROOT(t)); + struct iso_volume *vol = t->volset->volume[t->volnum]; + uint8_t one = 1; /* so that we can take &one */ + uint32_t zero = 0; + time_t never = -1; + uint8_t dir_record[34]; + + ecma119_write_dir_record(t, dir_record, 0, root, RECORD_TYPE_ROOT); + iso_struct_pack(ecma119_privol_fmt, buf, + &one, + "CD001", + &one, + system_id, + vol->volume_id.cstr, + &t->vol_space_size, + &t->volset->volset_size, + &t->volnum, + &t->block_size, + &t->path_table_size, + &t->l_path_table_pos, + &zero, + &t->m_path_table_pos, + &zero, + dir_record, + t->volset->volset_id.cstr, + vol->publisher_id.cstr, + vol->data_preparer_id.cstr, + application_id, + "", + "", + "", + &t->now, + &t->now, + &never, + &t->now, + &one); + + t->state = (t->joliet) ? ECMA119_WRITE_SUP_VOL_DESC_JOLIET : + ECMA119_WRITE_VOL_DESC_TERMINATOR; +} + +static void ecma119_write_supvol_desc_joliet(struct ecma119_write_target *t, + unsigned char *buf) +{ struct iso_tree_node *root = ISO_NODE(TARGET_ROOT(t)); + struct iso_volume *vol = t->volset->volume[t->volnum]; + uint8_t one = 1; /* so that we can take &one */ + uint8_t two = 2; + uint32_t zero = 0; + time_t never = -1; + uint8_t dir_record[34]; + + ecma119_write_dir_record(t, dir_record, ECMA119_JOLIET, + root, RECORD_TYPE_ROOT); + iso_struct_pack(ecma119_supvol_joliet_fmt, buf, + &two, + "CD001", + &one, + &zero, + system_id_joliet, + vol->volume_id.jstr, + &t->vol_space_size, + "%/E", + &t->volset->volset_size, + &t->volnum, + &t->block_size, + &t->path_table_size_joliet, + &t->l_path_table_pos_joliet, + &zero, + &t->m_path_table_pos_joliet, + &zero, + dir_record, + t->volset->volset_id.jstr, + vol->publisher_id.jstr, + vol->data_preparer_id.jstr, + application_id_joliet, + &zero, + &zero, + &zero, + &t->now, + &t->now, + &never, + &t->now, + &one); + + t->state = ECMA119_WRITE_VOL_DESC_TERMINATOR; +} + +static void ecma119_write_vol_desc_terminator(struct ecma119_write_target *t, + unsigned char *buf) +{ + buf[0] = 255; + strcpy((char*)&buf[1], "CD001"); + buf[6] = 1; + + t->state = ECMA119_WRITE_L_PATH_TABLE; +} + +/** + * Write a full path table to the buffer (it is assumed to be large enough). + * The path table will be broken into 2048-byte chunks later is necessary. + */ +static void ecma119_write_path_table_full(struct ecma119_write_target *t, + unsigned char *buf, + int flags, + int m_type) +{ + void (*write_int)(uint8_t*, uint32_t, int); + struct iso_tree_dir *dir; + const char *name; + int len, parent, i; + size_t off; + + node_namelen namelen; + node_getname getname; + off_t root_block; + struct iso_tree_dir **pathlist; + + if (flags & ECMA119_JOLIET) { + namelen = node_namelen_joliet; + getname = node_getname_joliet; + pathlist = t->pathlist_joliet; + root_block = GET_DIR_INF(pathlist[0])->joliet_block; + } else { + namelen = node_namelen_iso; + getname = node_getname_iso; + pathlist = t->pathlist; + root_block = pathlist[0]->block; + } + + write_int = m_type ? iso_msb : iso_lsb; + + /* write the root directory */ + buf[0] = 1; + buf[1] = 0; + write_int(&buf[2], root_block, 4); + write_int(&buf[6], 1, 2); + + /* write the rest */ + off = 10; + for (i=1; idirlist_len; i++) { + struct iso_tree_dir *dirparent; + off_t block; + + dir = pathlist[i]; + name = getname(ISO_NODE(dir)); + len = namelen(ISO_NODE(dir)); + if (flags & ECMA119_JOLIET) { + dirparent = GET_DIR_INF(dir)->real_parent; + block = GET_DIR_INF(dir)->joliet_block; + } else { + dirparent = dir->parent; + block = dir->block; + } + + for (parent=0; parentstate_data.path_table +static void ecma119_write_path_table(struct ecma119_write_target *t, + unsigned char *buf, + int flags, + int m_type) +{ + int path_table_size; + + if (flags & ECMA119_JOLIET) { + path_table_size = t->path_table_size_joliet; + } else { + path_table_size = t->path_table_size; + } + + if (!SDATA.data) { + SDATA.data = calloc(1, ROUND_UP(path_table_size, 2048)); + ecma119_write_path_table_full(t, SDATA.data, flags, m_type); + } + + memcpy(buf, SDATA.data + SDATA.blocks*2048, 2048); + SDATA.blocks++; + + if (SDATA.blocks*2048 >= path_table_size) { + free(SDATA.data); + SDATA.data = NULL; + SDATA.blocks = 0; + if (!t->joliet && m_type) { + t->state = ECMA119_WRITE_DIR_RECORDS; + } else { + t->state++; + } + } +} +#undef SDATA + +#define SDATA t->state_data.dir_records +static void ecma119_write_dir_records(struct ecma119_write_target *t, + unsigned char *buf, + int flags) +{ + struct iso_tree_dir **dirlist; + + if (flags & ECMA119_JOLIET) { + dirlist = t->dirlist_joliet; + } else { + dirlist = t->dirlist; + } + + if (!SDATA.data) { + struct iso_tree_dir *dir = dirlist[SDATA.dir++]; + struct dir_write_info *inf = GET_DIR_INF(dir); + + SDATA.data = ecma119_write_dir(t, flags, dir); + SDATA.pos = 0; + + if (flags & ECMA119_JOLIET) { + SDATA.data_len = inf->joliet_len; + } else { + SDATA.data_len = inf->len + inf->susp_len; + } + } + + memcpy(buf, SDATA.data + SDATA.pos, 2048); + SDATA.pos += 2048; + + if (SDATA.pos >= SDATA.data_len) { + free(SDATA.data); + SDATA.data = 0; + SDATA.pos = 0; + SDATA.data_len = 0; + if (SDATA.dir == t->dirlist_len) { + SDATA.dir = 0; + if (!t->joliet || (flags & ECMA119_JOLIET)) { + t->state = t->filelist_len ? ECMA119_WRITE_FILES + : ECMA119_WRITE_DONE; + } else { + t->state = ECMA119_WRITE_DIR_RECORDS_JOLIET; + } + } + } +} +#undef SDATA + +#define SDATA t->state_data.files +static void ecma119_write_files(struct ecma119_write_target *t, + unsigned char *buf) +{ + int numread; + + if (!SDATA.fd) { + struct iso_tree_file *f = t->filelist[SDATA.file++]; + assert(t->curblock == f->block); + SDATA.fd = fopen(f->path, "r"); + SDATA.data_len = f->attrib.st_size; + if (!SDATA.fd) { + fprintf(stderr, "Error: couldn't open file %s: %s\n", + f->path, strerror(errno)); + } + } + + if (SDATA.fd) { + numread = fread(buf, 1, 2048, SDATA.fd); + } else { + numread = t->block_size; + } + if (numread == -1) { + fprintf(stderr, "Error reading file: %s\n", strerror(errno)); + return; + } + SDATA.pos += numread; + + if (!SDATA.pos || SDATA.pos >= SDATA.data_len) { + fclose(SDATA.fd); + SDATA.data_len = 0; + SDATA.fd = NULL; + SDATA.pos = 0; + if (SDATA.file == t->filelist_len) { + SDATA.file = 0; + t->state = ECMA119_WRITE_DONE; + } + } +} +#undef SDATA + +static unsigned char *ecma119_write_dir(struct ecma119_write_target *t, + int flags, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = GET_DIR_INF(dir); + int len = ROUND_UP(inf->susp_len + inf->len, 2048); + unsigned char *buf; + int i, pos; + struct iso_tree_node *ch; + + susp_len slen; + total_dirent_len dlen; + struct iso_tree_node **children; + int nchildren; + + assert ( ((flags & ECMA119_JOLIET) && t->curblock == inf->joliet_block) + || t->curblock == dir->block ); + + if (flags & ECMA119_JOLIET) { + children = ecma119_sort_joliet(dir); + nchildren = inf->real_nchildren + dir->nfiles; + len = ROUND_UP(inf->joliet_len, 2048); + slen = susp_len_joliet; + dlen = dirent_len_joliet; + } else { + children = ecma119_mangle_names(dir); + nchildren = dir->nchildren + dir->nfiles; + len = ROUND_UP(inf->susp_len + inf->len, 2048); + slen = susp_len_iso; + dlen = dirent_len_iso; + } + + buf = calloc(1, len); + pos = 0; + + /* write all the dir records */ + ecma119_write_dir_record(t, buf+pos, flags, ISO_NODE(dir), + RECORD_TYPE_SELF); + pos += 34 + slen(&inf->self_susp); + ecma119_write_dir_record(t, buf+pos, flags, ISO_NODE(dir), + RECORD_TYPE_PARENT); + pos += 34 + slen(&inf->parent_susp); + + for (i=0; iattrib.st_mode ) + && GET_FILE_INF(ch)->real_me) { + continue; + } + ecma119_write_dir_record(t, buf+pos, flags, ch, + RECORD_TYPE_NORMAL); + pos += dlen(ch); + } + + if (flags & ECMA119_JOLIET) { + free(children); + return buf; + } + + /* write all the SUSP continuation areas */ + susp_write_CE(t, &inf->self_susp, buf+pos); + pos += inf->self_susp.CE_len; + susp_write_CE(t, &inf->parent_susp, buf+pos); + pos += inf->parent_susp.CE_len; + for (i=0; isusp, buf+pos); + pos += ninf->susp.CE_len; + } + free(children); + return buf; +} + +static void ecma119_write_dir_record(struct ecma119_write_target *t, + unsigned char *buf, + int flags, + struct iso_tree_node *node, + enum RecordType type) +{ + if (flags & ECMA119_JOLIET) { + ecma119_write_dir_record_joliet(t, buf, node, type); + } else { + ecma119_write_dir_record_iso(t, buf, node, type); + } +} + +static void ecma119_write_dir_record_iso(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type) +{ + struct node_write_info *inf = GET_NODE_INF(node); + uint8_t len_dr, len_fi, flags; + uint8_t vol_seq_num = t->volnum; + + if (type == RECORD_TYPE_NORMAL) { + len_fi = node_namelen_iso(node); + len_dr = 33 + len_fi + 1 - len_fi%2 + inf->susp.non_CE_len; + flags = (S_ISDIR(node->attrib.st_mode)) ? 2 : 0; + iso_struct_pack(ecma119_dir_record_fmt, buf, + &len_dr, + &zero, + &node->block, + &node->attrib.st_size, + &t->now, + &flags, + &zero, + &zero, + &vol_seq_num, + &len_fi); + iso_struct_pack_long(&buf[33], len_fi, '<', 'B', 0, 0, 0, + node_getname_iso(node)); + susp_write(t, &inf->susp, &buf[len_dr - inf->susp.non_CE_len]); + } else if (type == RECORD_TYPE_PARENT && node->parent) { + ecma119_write_dir_record_noname(t, buf, node, type, + node->parent->block, + node->parent->attrib.st_size, 0); + susp_write(t, &GET_DIR_INF(node)->parent_susp, &buf[34]); + } else { + ecma119_write_dir_record_noname(t, buf, node, type, + node->block, + node->attrib.st_size, 0); + if (type != RECORD_TYPE_ROOT) { + susp_write(t, &GET_DIR_INF(node)->self_susp, &buf[34]); + } + } +} + +static void ecma119_write_dir_record_joliet(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type) +{ + uint8_t len_dr, len_fi, flags; + uint8_t vol_seq_num = t->volnum; + uint32_t block, size; + + if (type == RECORD_TYPE_NORMAL) { + if (S_ISDIR(node->attrib.st_mode)) { + block = GET_DIR_INF(node)->joliet_block; + size = GET_DIR_INF(node)->joliet_len; + flags = 2; + } else { + block = node->block; + size = node->attrib.st_size; + flags = 0; + } + len_fi = node_namelen_joliet(node); + len_dr = 34 + len_fi; + iso_struct_pack(ecma119_dir_record_fmt, buf, + &len_dr, + &zero, + &block, + &size, + &t->now, + &flags, + &zero, + &zero, + &vol_seq_num, + &len_fi); + iso_struct_pack_long(&buf[33], len_fi, '<', 'B', 0, 0, 0, + node->name.joliet); + } else if (type == RECORD_TYPE_PARENT && node->parent) { + struct iso_tree_dir *p = GET_DIR_INF(node)->real_parent; + struct dir_write_info *inf = GET_DIR_INF(p); + ecma119_write_dir_record_noname(t, buf, node, type, + inf->joliet_block, + inf->joliet_len, + 1); + } else { + struct dir_write_info *inf = GET_DIR_INF(node); + ecma119_write_dir_record_noname(t, buf, node, type, + inf->joliet_block, + inf->joliet_len, + 1); + } +} + +/* this writes a directory record for a file whose RecordType is not + * RECORD_TYPE_NORMAL. Since this implies that we don't need to write a file + * id, the only difference between Joliet and non-Joliet records is whetheror + * not we write the SUSP fields. */ +static void ecma119_write_dir_record_noname(struct ecma119_write_target *t, + uint8_t *buf, + struct iso_tree_node *node, + enum RecordType type, + uint32_t block, + uint32_t size, + int joliet) +{ + int file_id; + uint8_t len_dr; + uint8_t flags = 2; + uint8_t len_fi = 1; + uint8_t zero = 0; + struct dir_write_info *inf = GET_DIR_INF(node); + + assert( type != RECORD_TYPE_NORMAL ); + switch(type) { + case RECORD_TYPE_ROOT: + file_id = 0; + len_dr = 34; + break; + case RECORD_TYPE_SELF: + file_id = 0; + len_dr = 34 + (joliet ? 0 : inf->self_susp.non_CE_len); + break; + case RECORD_TYPE_PARENT: + file_id = 1; + len_dr = 34 + (joliet ? 0 : inf->parent_susp.non_CE_len); + break; + case RECORD_TYPE_NORMAL: /* shut up warning */ + assert(0); + } + + assert(iso_struct_calcsize(ecma119_dir_record_fmt) == 33); + iso_struct_pack(ecma119_dir_record_fmt, buf, + &len_dr, + &zero, + &block, + &size, + &t->now, + &flags, /* file flags */ + &zero, + &zero, + &len_fi, /* vol seq number */ + &len_fi); /* len_fi */ + buf[33] = file_id; +} + +static void ecma119_setup_path_tables_iso(struct ecma119_write_target *t) +{ + int i, j, cur; + struct iso_tree_node **children; + + t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len); + t->pathlist[0] = TARGET_ROOT(t); + t->path_table_size = 10; /* root directory record */ + + cur = 1; + for (i=0; idirlist_len; i++) { + struct iso_tree_dir *dir = t->pathlist[i]; + children = ecma119_mangle_names(dir); + for (j=0; jnchildren + dir->nfiles; j++) { + if (S_ISDIR(children[j]->attrib.st_mode)) { + int len = 8 + node_namelen_iso(children[j]); + t->pathlist[cur++] = ISO_DIR(children[j]); + t->path_table_size += len + len % 2; + } + } + free(children); + } +} + +static void ecma119_setup_path_tables_joliet(struct ecma119_write_target *t) +{ + int i, j, cur; + struct iso_tree_node **children; + + t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len); + t->pathlist_joliet[0] = TARGET_ROOT(t); + t->path_table_size_joliet = 10; /* root directory record */ + + cur = 1; + for (i=0; idirlist_len; i++) { + struct iso_tree_dir *dir = t->pathlist_joliet[i]; + struct dir_write_info *inf = GET_DIR_INF(dir); + children = ecma119_sort_joliet(dir); + for (j=0; jreal_nchildren + dir->nfiles; j++) { + if (S_ISDIR(children[j]->attrib.st_mode)) { + int len = 8 + node_namelen_joliet(children[j]); + t->pathlist_joliet[cur++] = + ISO_DIR(children[j]); + t->path_table_size_joliet += len + len % 2; + } + } + } +} diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h new file mode 100755 index 0000000..bda01ad --- /dev/null +++ b/libisofs/ecma119.h @@ -0,0 +1,220 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file ecma119.h + * + * Structures and definitions used for writing an emca119 (ISO9660) compatible + * volume. + */ + +#ifndef __ISO_ECMA119 +#define __ISO_ECMA119 + +#include +#include +#include "susp.h" + +/** + * Persistent data for writing directories according to the ecma119 standard. + */ +struct dir_write_info +{ + struct susp_info susp; /**< \see node_write_info */ + struct susp_info self_susp; /**< SUSP data for this directory's + * "." entry. + */ + struct susp_info parent_susp; /**< SUSP data for this directory's + * ".." entry. + */ + + int len; /**< The combined length of all children's + * Directory Record lengths. This includes + * the System Use areas. + */ + int susp_len; /**< The combined length of all children's + * SUSP Continuation Areas. + */ + + /* the parent/child information prior to relocation */ + struct iso_tree_dir *real_parent; + int real_nchildren; + struct iso_tree_dir **real_children; + int real_depth; + + /* joliet information */ + int joliet_block; /**< The block at which the Joliet version of + * this directory will be written. + */ + int joliet_len; /**< The combined length of all children's + * Joliet Directory Record lengths. + */ +}; + +/** + * Persistent data for writing files according to the ecma119 standard. + */ +struct file_write_info +{ + struct susp_info susp; /**< \see node_write_info */ + + struct iso_tree_dir *real_me; /**< If this is non-NULL, the file is + * a placeholder for a relocated + * directory and this field points to + * that relocated directory. + */ +}; + +/** + * The fields in common between file_write_info and dir_write_info. + */ +struct node_write_info +{ + struct susp_info susp; /**< The SUSP data for this file. */ +}; + +/** + * 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 iso_volset *volset; + int volnum; + + time_t now; /**< Time at which writing began. */ + int 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; + + struct iso_tree_dir **dirlist; /* A pre-order list of directories + * (this is the order in which we write + * out directory records). + */ + struct iso_tree_dir **pathlist; /* A breadth-first list of directories. + * This is used for writing out the path + * tables. + */ + int dirlist_len; /* The length of the previous 2 lists. + */ + + struct iso_tree_file **filelist;/* A pre-order list of files with + * non-NULL paths and non-zero sizes. + */ + int 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 list might be different from + * the lists above. */ + struct iso_tree_dir **dirlist_joliet; + struct iso_tree_dir **pathlist_joliet; + + enum ecma119_write_state state; /* The current state of the writer. */ + + /* persistent data for the various states. Each struct should not be + * touched except for the writer of the relevant stage. When the writer + * of the relevant stage is finished, it should set all fields to 0. + */ + union + { + struct + { + int blocks; + unsigned char *data; + } path_table; + struct + { + size_t pos; /* The number of bytes we have written + * so far in the current directory. + */ + size_t data_len;/* The number of bytes in the current + * directory. + */ + unsigned char *data; /* The data (combined Directory + * Records and susp_CE areas) of the + * current directory. + */ + int dir; /* The index in dirlist that we are + * currently writing. */ + } dir_records; + struct + { + size_t pos; /* The number of bytes we have written + * so far in the current file. + */ + size_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. */ + } files; + } state_data; +}; + +/** + * 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); + +/** Macros to help with casting between node_write_info and dir/file_write_info. + */ +#define DIR_INF(a) ( (struct dir_write_info*) (a) ) +#define FILE_INF(a) ( (struct file_write_info*) (a) ) +#define NODE_INF(a) ( (struct node_write_info*) (a) ) + +#define GET_DIR_INF(a) ( (struct dir_write_info*) (a)->writer_data ) +#define GET_FILE_INF(a) ( (struct file_write_info*) (a)->writer_data ) +#define GET_NODE_INF(a) ( (struct node_write_info*) (a)->writer_data ) + +#define TARGET_ROOT(t) ( (t)->volset->volume[(t)->volnum]->root ) + +#define NODE_NAMELEN(n,i) strlen(iso_tree_node_get_name(ISO_NODE(n), i)) +#define NODE_JOLLEN(n) ucslen(iso_tree_node_get_name(ISO_NODE(n), \ + ISO_NAME_JOLIET)) + + +#endif /* __ISO_ECMA119 */ diff --git a/libisofs/errors.c b/libisofs/errors.c new file mode 100755 index 0000000..860496f --- /dev/null +++ b/libisofs/errors.c @@ -0,0 +1,14 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "errors.h" +#include + +void iso_warn(enum iso_warnings w) +{ + printf("WARNING: %u\n", w); +} + +void iso_error(enum iso_errors e) +{ + printf("ERROR: %u\n", e); +} diff --git a/libisofs/errors.h b/libisofs/errors.h new file mode 100755 index 0000000..d7a073c --- /dev/null +++ b/libisofs/errors.h @@ -0,0 +1,19 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __ERRORS +#define __ERRORS + +enum iso_warnings +{ + ISO_WARNING_FOO +}; + +enum iso_errors +{ + ISO_ERROR_FOO +}; + +void iso_warn(enum iso_warnings w); +void iso_error(enum iso_errors e); + +#endif /* __ERRORS */ diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h new file mode 100755 index 0000000..064b03e --- /dev/null +++ b/libisofs/libisofs.h @@ -0,0 +1,243 @@ +/* -*- 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 __LIBISOFS +#define __LIBISOFS + +#include "libburn/libburn.h" + +/** + * Data volume. + * @see volume.h for details. + */ +struct iso_volume; + +/** + * A set of data volumes. + * @see volume.h for details. + */ +struct iso_volset; + +/** + * Directory on a volume. + * @see tree.h for details. + */ +struct iso_tree_dir; + +/** + * File on a volume. + * @see tree.h for details. + */ +struct iso_tree_file; + +/** + * Either a file or a directory. + * \see tree.h + */ +struct iso_tree_node; + +/** + * Possible versions of a file or directory name or identifier. + */ +enum iso_name_version { + ISO_NAME_FULL, /**< In the current locale. */ + ISO_NAME_ISO, /**< Current ISO level identifier. */ + ISO_NAME_ISO_L1, /**< ISO level 1 identifier. */ + ISO_NAME_ISO_L2, /**< ISO level 2 identifier. */ + ISO_NAME_ROCKRIDGE, /**< Rock Ridge file or directory name. */ + ISO_NAME_JOLIET /**< Joliet identifier. */ +}; + +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); + +/** + * Free a volume. + */ +void iso_volume_free(struct iso_volume *volume); + +/** + * Get the root directory for a volume. + */ +struct iso_tree_dir *iso_volume_get_root(const struct iso_volume *volume); + +/** + * Fill in the volume identifier for a volume. + */ +void iso_volume_set_volume_id(struct iso_volume *volume, + const char *volume_id); + +/** + * Fill in the publisher for a volume. + */ +void iso_volume_set_publisher_id(struct iso_volume *volume, + const char *publisher_id); + +/** + * Fill in the data preparer for a volume. + */ +void iso_volume_set_data_preparer_id(struct iso_volume *volume, + const char *data_preparer_id); + +/** + * Get the current ISO level for a volume. + */ +int iso_volume_get_iso_level(const struct iso_volume *volume); + +/** + * Set the current ISO level for a volume. + * ISO level must be 1 or 2. + */ +void iso_volume_set_iso_level(struct iso_volume *volume, int level); + +/** + * See if Rock Ridge (POSIX) is enabled for a volume. + */ +int iso_volume_get_rockridge(const struct iso_volume *volume); + +/** + * Enable or disable Rock Ridge (POSIX) for a volume. + */ +void iso_volume_set_rockridge(struct iso_volume *volume, int rockridge); + +/** + * See if Joliet (Unicode) is enabled for a volume. + */ +int iso_volume_get_joliet(const struct iso_volume *volume); + +/** + * Enable or disable Joliet (Unicode) for a volume. + */ +void iso_volume_set_joliet(struct iso_volume *volume, int joliet); + +/** + * 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 non-NULL + * \pre \p path is non-NULL and is a valid path to a non-directory on the local + * filesystem. + * \return An iso_tree_file whose path is \p path and whose parent is \p parent. + */ +struct iso_tree_file *iso_tree_add_file(struct iso_tree_dir *parent, + const char *path); + +/** + * Add a directory from the local filesystem to the tree. + * Warning: this only adds the directory itself, no files or subdirectories. + * + * \param path The path, on the local filesystem, of the directory. + * + * \pre \p parent is non-NULL + * \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_dir *iso_tree_add_dir(struct iso_tree_dir *parent, + const char *path); + +/** + * Recursively add an existing directory to the tree. + * Warning: when using this, you'll lose pointers to files or subdirectories. + * If you want to have pointers to all files and directories, + * use iso_tree_add_file and iso_tree_add_dir. + * + * \param path The path, on the local filesystem, of the directory to add. + * + * \pre \p parent is non-NULL + * \pre \p path is non-NULL and is a valid path to a directory on the local + * filesystem. + * \return a pointer to the newly created directory. + */ +struct iso_tree_dir *iso_tree_radd_dir(struct iso_tree_dir *parent, + const char *path); + +/** + * Creates a new, empty directory on the volume. + * + * \pre \p parent is non-NULL + * \pre \p name is unique among the children and files belonging to \p parent. + * Also, it doesn't contain '/' characters. + * + * \post \p parent contains a child directory whose name is \p name and whose + * POSIX attributes are the same as \p parent's. + * \return a pointer to the newly created directory. + */ +struct iso_tree_dir *iso_tree_add_new_dir(struct iso_tree_dir *parent, + const char *name); + +/** + * Get the name of a node. + */ +const char *iso_tree_node_get_name(const struct iso_tree_node *node, + enum iso_name_version ver); + +/** + * Set the name of a file. + * The name you input here will be the full name and will be used to derive the + * ISO, RockRidge and Joliet names. + */ +void iso_tree_file_set_name(struct iso_tree_file *file, const char *name); + +/** + * Set the name of a directory. + * The name you input here will be the full name and will be used to derive the + * ISO, RockRidge and Joliet names. + */ +void iso_tree_dir_set_name(struct iso_tree_dir *dir, 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_dir *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 /* __LIBISOFS */ diff --git a/libisofs/rockridge.c b/libisofs/rockridge.c new file mode 100755 index 0000000..d6bab25 --- /dev/null +++ b/libisofs/rockridge.c @@ -0,0 +1,299 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "rockridge.h" +#include "tree.h" +#include "util.h" +#include "volume.h" +#include "ecma119.h" +#include "susp.h" + +#include +#include +#include +#include +#include +#include + +/* create a PX field from the permissions on the current node. */ +unsigned char *rrip_make_PX(struct ecma119_write_target *t, + struct iso_tree_node *node) +{ + unsigned char *PX = malloc(44); + + PX[0] = 'P'; + PX[1] = 'X'; + PX[2] = 44; + PX[3] = 1; + iso_bb(&PX[4], node->attrib.st_mode, 4); + iso_bb(&PX[12], node->attrib.st_nlink, 4); + iso_bb(&PX[20], node->attrib.st_uid, 4); + iso_bb(&PX[28], node->attrib.st_gid, 4); + iso_bb(&PX[36], node->attrib.st_ino, 4); + return PX; +} + +/** See IEEE 1282 4.1.1 */ +void rrip_add_PX(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + susp_append(t, node, rrip_make_PX(t, node)); +} + +void rrip_add_PX_dir(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + susp_append(t, ISO_NODE(dir), rrip_make_PX(t, ISO_NODE(dir))); + susp_append_self(t, dir, rrip_make_PX(t, ISO_NODE(dir))); + susp_append_parent(t, dir, rrip_make_PX(t, ISO_NODE(dir))); +} + +void rrip_add_PN(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + unsigned char *PN = malloc(20); + + PN[0] = 'P'; + PN[1] = 'N'; + PN[2] = 20; + PN[3] = 1; + iso_bb(&PN[4], node->attrib.st_dev >> 32, 4); + iso_bb(&PN[12], node->attrib.st_dev & 0xffffffff, 4); + susp_append(t, node, PN); +} + +static void rrip_SL_append_comp(int *n, unsigned char ***comps, + char *s, int size, char fl) +{ + unsigned char *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, + unsigned char ***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 iso_tree_node *node) +{ + int ret, pathsize = 0; + char *path = NULL, *cur, *prev; + struct iso_tree_file *file = (struct iso_tree_file *)node; + int i, j; + + unsigned char **comp = NULL; + int n_comp = 0; + int total_comp_len = 0; + int written = 0, pos; + + unsigned char *SL; + + do { + pathsize += 128; + path = realloc(path, pathsize); + ret = readlink(file->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, 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, 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 iso_tree_node *node, + char *name, int size, int flags) +{ + unsigned char *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, node, NM); +} + +void rrip_add_NM(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + struct iso_tree_file *file = (struct iso_tree_file *)node; + int len = strlen(file->name.rockridge); + char *pos = file->name.rockridge; + + if (len == 1 && pos[0] == '.') { + rrip_add_NM_single(t, node, pos, 0, 1 << 1); + return; + } + if (len == 2 && !strncmp(pos, "..", 2)) { + rrip_add_NM_single(t, node, pos, 0, 1 << 2); + return; + } + + while (len > 250) { + rrip_add_NM_single(t, node, pos, 250, 1); + len -= 250; + pos += 250; + } + rrip_add_NM_single(t, node, pos, len, 0); +} + +void rrip_add_CL(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + unsigned char *CL = calloc(1, 12); + + CL[0] = 'C'; + CL[1] = 'L'; + CL[2] = 12; + CL[3] = 1; + susp_append(t, node, CL); +} + +void rrip_add_PL(struct ecma119_write_target *t, struct iso_tree_dir *node) +{ + unsigned char *PL = calloc(1, 12); + + PL[0] = 'P'; + PL[1] = 'L'; + PL[2] = 12; + PL[3] = 1; + susp_append_parent(t, node, PL); +} + +void rrip_add_RE(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + unsigned char *RE = malloc(4); + + RE[0] = 'R'; + RE[1] = 'E'; + RE[2] = 4; + RE[3] = 1; + susp_append(t, node, RE); +} + +void rrip_add_TF(struct ecma119_write_target *t, struct iso_tree_node *node) +{ + unsigned char *TF = malloc(5 + 3 * 17); + + TF[0] = 'T'; + TF[1] = 'F'; + TF[2] = 5 + 3 * 17; + TF[3] = 1; + TF[4] = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 7); + iso_datetime_17(&TF[5], node->attrib.st_mtime); + iso_datetime_17(&TF[22], node->attrib.st_atime); + iso_datetime_17(&TF[39], node->attrib.st_ctime); + susp_append(t, node, TF); +} + +void rrip_finalize(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + struct dir_write_info *inf; + struct file_write_info *finf; + int i; + + inf = dir->writer_data; + if (dir->parent != inf->real_parent) { + unsigned char *PL = susp_find(&inf->parent_susp, "PL"); + + iso_bb(&PL[4], inf->real_parent->block, 4); + } + + for (i = 0; i < dir->nfiles; i++) { + finf = dir->files[i]->writer_data; + if (finf->real_me) { + unsigned char *CL = susp_find(&finf->susp, "CL"); + + iso_bb(&CL[4], finf->real_me->block, 4); + } + } + + for (i = 0; i < dir->nchildren; i++) { + rrip_finalize(t, dir->children[i]); + } +} diff --git a/libisofs/rockridge.h b/libisofs/rockridge.h new file mode 100755 index 0000000..ba8e894 --- /dev/null +++ b/libisofs/rockridge.h @@ -0,0 +1,30 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** Functions and structures used for Rock Ridge support. */ + +#ifndef __ISO_ROCKRIDGE +#define __ISO_ROCKRIDGE + +struct ecma119_write_target; +struct iso_tree_node; +struct iso_tree_dir; + +void rrip_add_PX(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_PN(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_SL(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_NM(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_CL(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_RE(struct ecma119_write_target *, struct iso_tree_node *); +void rrip_add_TF(struct ecma119_write_target *, struct iso_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 iso_tree_dir *); + +/* Add a PX field to the susp, self_susp and parent_susp entries */ +void rrip_add_PX_dir(struct ecma119_write_target *, struct iso_tree_dir *); + +void rrip_finalize(struct ecma119_write_target *, struct iso_tree_dir *); + +#endif /* __ISO_ROCKRIDGE */ diff --git a/libisofs/struct.c b/libisofs/struct.c new file mode 100755 index 0000000..1d8e388 --- /dev/null +++ b/libisofs/struct.c @@ -0,0 +1,340 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "struct.h" +#include "util.h" +#include +#include +#include +#include +#include + +struct struct_element { + uint8_t ch; + + int bytes; /* The number of bytes in the value to convert + * from/to. */ + uint8_t end; /* The endianness specifier. */ + int mul; /* The number of values to convert. */ + + union { /* Pointer to the value. */ + uint8_t *val8; + uint16_t *val16; + uint32_t *val32; + time_t *time; + } val; +}; + +/* check if a character is a valid endian-ness specifier */ +#define isend(a) ((a) == '=' || (a) == '<' || (a) == '>') + +static int iso_struct_element_make(struct struct_element *elem, + int mul, + char end, + char ch) +{ + if (!end) { +#ifdef WORDS_BIGENDIAN + elem->end = '>'; /* default endianness is native */ +#else + elem->end = '<'; +#endif + } else { + elem->end = end; + } + + elem->ch = ch; + elem->mul = mul; + elem->val.val8 = NULL; + switch(toupper(ch)) { + case 'X': + case 'B': + elem->bytes = 1; + break; + case 'H': + elem->bytes = 2; + break; + case 'L': + elem->bytes = 4; + break; + case 'S': + elem->bytes = 7; + elem->end = '<'; + break; + case 'T': + elem->bytes = 17; + elem->end = '<'; + break; + default: + elem->bytes = -1; + break; + } + return elem->bytes * elem->mul * ((elem->end == '=') ? 2 : 1); +} + +static int iso_struct_element_make_v(struct struct_element *elem, + va_list *ap) +{ + int mul = va_arg(*ap, int); + int end = va_arg(*ap, int); + int ch = va_arg(*ap, int); + return iso_struct_element_make(elem, mul, end, ch); +} + +static int iso_struct_element_parse(const char **ffmt, + struct struct_element *elem) +{ + off_t pos; + const char *fmt = *ffmt; + int mul; + char end = 0; + + mul = 1; + for (pos=0; isdigit(fmt[pos]) || isend(fmt[pos]); pos++) { + if (isdigit(fmt[pos])) { + mul = atoi( fmt + pos ); + while (isdigit(fmt[pos+1])) pos++; + } else { + end = fmt[pos]; + } + } + + (*ffmt) += pos + 1; + + return iso_struct_element_make(elem, mul, end, fmt[pos]); +} + +/* read a single integer from data[i] to elem[i], interpreting the endian-ness + * and offset appropriately. */ +static uint32_t iso_struct_element_read_int(struct struct_element *elem, + const uint8_t *data, + int i) +{ + uint32_t el; + + switch(elem->end) { + case '>': + el = iso_read_msb(data + i*elem->bytes, elem->bytes); + break; + case '<': + el = iso_read_lsb(data + i*elem->bytes, elem->bytes); + break; + case '=': + el = iso_read_bb(data + i*elem->bytes*2, elem->bytes); + } + + switch(elem->bytes) { + case 1: + elem->val.val8[i] = el; + break; + case 2: + elem->val.val16[i] = el; + break; + case 4: + elem->val.val32[i] = el; + break; + } + + return el; +} + +/* write a single integer from elem[i] to data[i]. */ +static uint32_t iso_struct_element_write1(struct struct_element *elem, + uint8_t *data, + int i) +{ + uint32_t el; + + switch(elem->bytes) { + case 1: + el = elem->val.val8[i]; + break; + case 2: + el = elem->val.val16[i]; + break; + case 4: + el = elem->val.val32[i]; + break; + } + + switch(elem->end) { + case '>': + iso_msb(data + i*elem->bytes, el, elem->bytes); + break; + case '<': + iso_lsb(data + i*elem->bytes, el, elem->bytes); + break; + case '=': + iso_bb(data + i*elem->bytes*2, el, elem->bytes); + } + + return el; +} + +static int iso_struct_element_read(struct struct_element *elem, + const uint8_t *data) +{ + int size = elem->bytes * ((elem->end == '=') ? 2 : 1); + int i; + + if (elem->ch == 'x') { + return size * elem->mul; + } + + for (i=0; imul; i++) { + switch(toupper(elem->ch)) { + case 'S': + /* + elem->val.time[i] = iso_datetime_read_7(&data[i*7]); + */ + break; + case 'T': + /* + elem->val.time[i] = iso_datetime_read_17(&data[i*17]); + */ + break; + default: + iso_struct_element_read_int(elem, data, i); + } + } + + return size * elem->mul; +} + +static int iso_struct_element_write(struct struct_element *elem, + uint8_t *data) +{ + int size = elem->bytes * ((elem->end == '=') ? 2 : 1); + int i; + uint32_t ret; + + if (elem->ch == 'x') { + return size*elem->mul; + } + + for (i=0; imul; i++) { + switch(toupper(elem->ch)) { + case 'S': + iso_datetime_7(&data[i*7], elem->val.time[i]); + ret = elem->val.time[i]; + break; + case 'T': + iso_datetime_17(&data[i*17], elem->val.time[i]); + ret = elem->val.time[i]; + break; + default: + ret = iso_struct_element_write1(elem, data, i); + break; + } + + if (islower(elem->ch) && ret == 0) { + memset(data + size*i, 0, size*(elem->mul-i)); + break; + } + } + + return size * elem->mul; +} + +int iso_struct_unpack(const char *fmt, const uint8_t *data, ...) +{ + int num_conv; + int ret; + va_list ap; + struct struct_element elem; + off_t off; + + va_start(ap, data); + num_conv = 0; + off = 0; + while(*fmt) { + ret = iso_struct_element_parse(&fmt, &elem); + if (ret < 0) { + va_end(ap); + return -1; + } + if (elem.ch != 'x') { + elem.val.val8 = va_arg(ap, void*); + } + off += iso_struct_element_read(&elem, data + off); + num_conv++; + } + + va_end(ap); + return num_conv; +} + +int iso_struct_pack(const char *fmt, uint8_t *data, ...) +{ + int num_conv; + int ret; + va_list ap; + struct struct_element elem; + off_t off; + + va_start(ap, data); + num_conv = 0; + off = 0; + while(*fmt) { + ret = iso_struct_element_parse(&fmt, &elem); + if (ret < 0) { + va_end(ap); + return -1; + } + if (elem.ch != 'x') { + elem.val.val8 = va_arg(ap, void*); + } + off += iso_struct_element_write(&elem, data + off); + num_conv++; + } + + va_end(ap); + return num_conv; +} + +int iso_struct_pack_long(uint8_t *data, ...) +{ + int num_conv; + int ret; + int i, j; + va_list ap; + struct struct_element *elem = NULL; + off_t off; + + va_start(ap, data); + num_conv = 0; + off = 0; + + elem = calloc(1, sizeof(struct struct_element)); + i=0; + while ((ret = iso_struct_element_make_v(&elem[i], &ap) > 0)) { + elem = realloc(elem, (++i + 1) * sizeof(struct struct_element)); + } + for (j=0; j big-endian + * = both-endian (ie. according to ecma119 7.2.3 or 7.3.3) + * + * Each conversion specifier may also be preceded by a length specifier. For + * example, "<5L" specifies an array of 5 little-endian 32-bit integers. Note + * that "=L" takes 8 bytes while "L" each take 4. + * + * You can use a lower-case conversion specifier instead of an upper-case one + * to signify that the (multi-element) conversion should stop when a zero is + * reached. This is useful for writing out NULL-terminated strings. Note that + * this has no effect when unpacking data from a struct. + */ + +#ifndef __ISO_STRUCT +#define __ISO_STRUCT + +#include + +/** + * Unpack a struct into its components. The list of components is a list of + * pointers to the variables to write. + * + * For example: + * uint8_t byte1, byte2; + * uint16_t uint; + * iso_struct_unpack("BB=H", data, &byte1, &byte2, &uint); + * + * \return The number of conversions performed, or -1 on error. + */ +int iso_struct_unpack(const char *fmt, const uint8_t *data, ...); + +/** + * Write out a struct from its components. The list of components is a list of + * pointers to the variables to write and the buffer to which to write + * is assumed to be large + * enough to take the data. + * + * \return The number of conversions performed, or -1 on error. + */ +int iso_struct_pack(const char *fmt, uint8_t *data, ...); + +/** + * Achieves the same effect as iso_struct_pack(), but the format is passed as + * a sequence of (int, char, char) triples. This list is terminated by + * (0, 0, 0) and the list of parameters follows. + * + * Example: iso_struct_pack_long(data, 4, '=', 'H', 0, 0, 0, &val) is the same + * as iso_struct_pack("4=H", 0, 0, 0, &val) + */ +int iso_struct_pack_long(uint8_t *data, ...); + +/** + * Calculate the size of a given format string. + * + * \return The sum of the length of all formats in the string, in bytes. Return + * -1 on error. + */ +int iso_struct_calcsize(const char *fmt); + +#endif diff --git a/libisofs/susp.c b/libisofs/susp.c new file mode 100755 index 0000000..e67d39f --- /dev/null +++ b/libisofs/susp.c @@ -0,0 +1,312 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "susp.h" +#include "tree.h" +#include "util.h" +#include "ecma119.h" + +#include +#include +#include + +static void susp_insert_direct(struct ecma119_write_target *t, + struct susp_info *susp, unsigned char *data, + int pos) +{ + int i; + + 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 iso_tree_node *node, unsigned char *data) +{ + struct dir_write_info *inf = node->writer_data; + struct susp_info *susp = &inf->susp; + + susp_insert_direct(t, susp, data, susp->n_susp_fields); +} + +void susp_append_self(struct ecma119_write_target *t, + struct iso_tree_dir *dir, unsigned char *data) +{ + struct dir_write_info *inf = dir->writer_data; + struct susp_info *susp = &inf->self_susp; + + susp_insert_direct(t, susp, data, susp->n_susp_fields); +} + +void susp_append_parent(struct ecma119_write_target *t, + struct iso_tree_dir *dir, unsigned char *data) +{ + struct dir_write_info *inf = dir->writer_data; + struct susp_info *susp = &inf->parent_susp; + + susp_insert_direct(t, susp, data, susp->n_susp_fields); +} + +void susp_insert(struct ecma119_write_target *t, + struct iso_tree_node *node, unsigned char *data, int pos) +{ + struct dir_write_info *inf = node->writer_data; + struct susp_info *susp = &inf->susp; + + susp_insert_direct(t, susp, data, pos); +} + +void susp_insert_self(struct ecma119_write_target *t, + struct iso_tree_dir *dir, unsigned char *data, int pos) +{ + struct dir_write_info *inf = dir->writer_data; + struct susp_info *susp = &inf->self_susp; + + susp_insert_direct(t, susp, data, pos); +} + +void susp_insert_parent(struct ecma119_write_target *t, + struct iso_tree_dir *dir, unsigned char *data, int pos) +{ + struct dir_write_info *inf = dir->writer_data; + struct susp_info *susp = &inf->parent_susp; + + susp_insert_direct(t, susp, data, pos); +} + +unsigned char *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. */ +#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; +} + +/** 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 iso_tree_node *node) +{ + struct dir_write_info *inf = node->writer_data; + unsigned char *CE; + + CE = susp_add_single_CE(t, &inf->susp, 255 - node->dirent_len); + if (CE) + susp_insert(t, node, CE, inf->susp.n_fields_fit - 1); + if (S_ISDIR(node->attrib.st_mode)) { + CE = susp_add_single_CE(t, &inf->self_susp, 255 - 34); + if (CE) + susp_insert_self(t, (struct iso_tree_dir *)node, CE, + inf->self_susp.n_fields_fit - 1); + CE = susp_add_single_CE(t, &inf->parent_susp, 255 - 34); + if (CE) + susp_insert_parent(t, (struct iso_tree_dir *)node, CE, + inf->parent_susp.n_fields_fit - 1); + } +} + +/** See IEEE P1281 Draft Version 1.12/5.3 */ +void susp_add_SP(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + unsigned char *SP = malloc(7); + + 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_self(t, dir, 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 */ +void susp_add_ER(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + unsigned char *ER = malloc(182); + + 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_self(t, dir, ER); +} + +static void susp_fin_CE(struct ecma119_write_target *t, + struct iso_tree_dir *dir) +{ + struct dir_write_info *inf = (struct dir_write_info *) + dir->writer_data; + struct node_write_info *cinf; + unsigned char *CE; + int i; + int CE_offset = inf->len; + + if (inf->self_susp.CE_len) { + CE = inf->self_susp.susp_fields[inf->self_susp.n_fields_fit - + 1]; + iso_bb(&CE[4], dir->block + CE_offset / 2048, 4); + iso_bb(&CE[12], CE_offset % 2048, 4); + CE_offset += inf->self_susp.CE_len; + } + if (inf->parent_susp.CE_len) { + CE = inf->parent_susp.susp_fields[inf->parent_susp. + n_fields_fit - 1]; + iso_bb(&CE[4], dir->block + CE_offset / 2048, 4); + iso_bb(&CE[12], CE_offset % 2048, 4); + CE_offset += inf->parent_susp.CE_len; + } + + for (i = 0; i < dir->nchildren; i++) { + cinf = dir->children[i]->writer_data; + if (!cinf->susp.CE_len) { + continue; + } + CE = cinf->susp.susp_fields[cinf->susp.n_fields_fit - 1]; + iso_bb(&CE[4], dir->block + CE_offset / 2048, 4); + iso_bb(&CE[12], CE_offset % 2048, 4); + CE_offset += cinf->susp.CE_len; + } + for (i = 0; i < dir->nfiles; i++) { + cinf = dir->files[i]->writer_data; + if (!cinf->susp.CE_len) { + continue; + } + CE = cinf->susp.susp_fields[cinf->susp.n_fields_fit - 1]; + iso_bb(&CE[4], dir->block + CE_offset / 2048, 4); + iso_bb(&CE[12], CE_offset % 2048, 4); + CE_offset += cinf->susp.CE_len; + } + assert(CE_offset == inf->len + inf->susp_len); +} + +void susp_finalize(struct ecma119_write_target *t, struct iso_tree_dir *dir) +{ + int i; + + if (dir->depth != 1) { + susp_fin_CE(t, dir); + } + + for (i = 0; i < dir->nchildren; i++) { + susp_finalize(t, 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..7894581 --- /dev/null +++ b/libisofs/susp.h @@ -0,0 +1,75 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** Functions and structures used for SUSP (IEEE 1281). + */ + +#ifndef __ISO_SUSP +#define __ISO_SUSP + +/* SUSP is only present in standard ecma119 */ +struct ecma119_write_target; +struct iso_tree_node; +struct iso_tree_dir; + +/** 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 */ + unsigned char **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 iso_tree_node *); + +/* these next 2 are special because they don't modify the susp fields of the + * directory that gets passed to them; they modify the susp fields of the + * "." entry in the directory. */ +void susp_add_SP(struct ecma119_write_target *, struct iso_tree_dir *); +void susp_add_ER(struct ecma119_write_target *, struct iso_tree_dir *); + +/** 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 iso_tree_dir *); + +void susp_append(struct ecma119_write_target *, + struct iso_tree_node *, + unsigned char *); +void susp_append_self(struct ecma119_write_target *, + struct iso_tree_dir *, + unsigned char *); +void susp_append_parent(struct ecma119_write_target *, + struct iso_tree_dir *, + unsigned char *); +void susp_insert(struct ecma119_write_target *, + struct iso_tree_node *, + unsigned char *, + int pos); +void susp_insert_self(struct ecma119_write_target *, + struct iso_tree_dir *, + unsigned char *, + int pos); +void susp_insert_parent(struct ecma119_write_target *, + struct iso_tree_dir *, + unsigned char *, + int pos); +unsigned char *susp_find(struct susp_info *, + const char *); + +void susp_write(struct ecma119_write_target *, + struct susp_info *, + unsigned char *); +void susp_write_CE(struct ecma119_write_target *, + struct susp_info *, + unsigned char *); + +void susp_free_fields(struct susp_info *); + +#endif /* __ISO_SUSP */ diff --git a/libisofs/test.c b/libisofs/test.c new file mode 100755 index 0000000..ff74099 --- /dev/null +++ b/libisofs/test.c @@ -0,0 +1,138 @@ +/* -*- 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/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 burn_source *src; + unsigned char buf[2048]; + FILE *fd; + int c; + int level=1, flags=0; + DIR *dir; + struct dirent *ent; + + 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 ("must pass directory to build iso from\n"); + usage(); + return 1; + } + if (argc < 3) { + printf ("must supply output file\n"); + usage(); + return 1; + } + fd = fopen(argv[optind+1], "w"); + if (!fd) { + perror("error opening output file"); + exit(1); + } + + volume = iso_volume_new( "VOLID", "PUBID", "PREPID" ); + volset = iso_volset_new( volume, "VOLSETID" ); + dir = opendir(argv[optind]); + if (!dir) { + perror("error opening input directory"); + exit(1); + } + + while ( (ent = readdir(dir)) ) { + struct stat st; + char *name; + + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { + continue; + } + + name = malloc(strlen(argv[optind]) + strlen(ent->d_name) + 2); + strcpy(name, argv[optind]); + strcat(name, "/"); + strcat(name, ent->d_name); + if (lstat(name, &st) == -1) { + fprintf(stderr, "error opening file %s: %s\n", + name, strerror(errno)); + exit(1); + } + + if (S_ISDIR(st.st_mode)) { + iso_tree_radd_dir(iso_volume_get_root(volume), name); + } else { + iso_tree_add_file(iso_volume_get_root(volume), name); + } + free(name); + } + + iso_tree_print(iso_volume_get_root(volume), 0); + + 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/libisofs/tree.c b/libisofs/tree.c new file mode 100755 index 0000000..1d5c486 --- /dev/null +++ b/libisofs/tree.c @@ -0,0 +1,409 @@ +/* -*- 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 +#include +#include + +#include "libisofs.h" +#include "tree.h" +#include "util.h" +#include "volume.h" + +struct iso_tree_dir *iso_tree_new_root(struct iso_volume *volume) +{ + struct iso_tree_dir *dir; + + assert(volume); + + dir = calloc(1, sizeof(struct iso_tree_dir)); + dir->attrib.st_mode = S_IFDIR; + dir->volume = volume; + + return dir; +} + +void iso_tree_free(struct iso_tree_dir *root) +{ + int i; + + assert(root); + + /* Free names. */ + free(root->name.full); + free(root->name.iso1); + free(root->name.iso2); + free(root->name.rockridge); + free(root->name.joliet); + + /* Free the children. */ + for (i = 0; i < root->nchildren; i++) + iso_tree_free(root->children[i]); + free(root->children); + + /* Free all files. */ + for (i = 0; i < root->nfiles; i++) { + struct iso_tree_file *file = root->files[i]; + + free(file->path); + free(file->name.full); + free(file->name.iso1); + free(file->name.iso2); + free(file->name.rockridge); + free(file->name.joliet); + free(file); + } + free(root->files); + + if (root->writer_data) { + fprintf(stderr, "Warning: freeing a directory with non-NULL " + "writer_data\n"); + } + + /* Free ourself. */ + free(root); +} + +struct iso_tree_file *iso_tree_add_new_file(struct iso_tree_dir *parent, + const char *name) +{ + struct iso_tree_file *file; + + assert(parent && name); + + file = calloc(1, sizeof(struct iso_tree_file)); + file->path = calloc(1, 1); + file->parent = parent; + file->volume = parent->volume; + file->attrib = parent->attrib; + file->attrib.st_mode &= ~S_IFMT; + file->attrib.st_mode |= S_IFREG; + file->attrib.st_size = 0; + iso_tree_file_set_name(file, name); + + /* Add the new file to the parent directory */ + parent->nfiles++; + parent->files = realloc(parent->files, sizeof(void *) * parent->nfiles); + parent->files[parent->nfiles - 1] = file; + + return file; +} + +struct iso_tree_file *iso_tree_add_file(struct iso_tree_dir *parent, + const char *path) +{ + struct iso_tree_file *file; + struct stat st; + int statret; + const char *name; + + assert( parent != NULL && path != NULL ); + statret = lstat(path, &st); + if (statret == -1) { + fprintf(stderr, "couldn't stat file %s: %s\n", + path, strerror(errno)); + return NULL; + } + + /* Set up path, parent and volume. */ + file = calloc(1, sizeof(struct iso_tree_file)); + file->path = strdup(path); + file->parent = parent; + file->volume = parent->volume; + file->attrib = st; + + /* find the last component in the path */ + name = strrchr(path, '/'); + if (name == NULL) { + name = path; + } else { + name++; + } + iso_tree_file_set_name(file, name); + + if (!S_ISREG(st.st_mode)) { + file->attrib.st_size = 0; + } + + /* Add the new file to the parent directory */ + parent->nfiles++; + parent->files = realloc(parent->files, sizeof(void *) * parent->nfiles); + parent->files[parent->nfiles - 1] = file; + + return file; +} + +struct iso_tree_dir *iso_tree_add_dir(struct iso_tree_dir *parent, + const char *path) +{ + struct iso_tree_dir *dir; + struct stat st; + int statret; + char *pathcpy; + char *name; + + assert( parent && path ); + statret = stat(path, &st); + if (statret == -1) { + fprintf(stderr, "couldn't stat directory %s: %s\n", + path, strerror(errno)); + return NULL; + } + + dir = calloc(1, sizeof(struct iso_tree_dir)); + dir->parent = parent; + dir->volume = parent->volume; + dir->attrib = st; + + /* find the last component in the path. We need to copy the path because + * we modify it if there is a trailing slash. */ + pathcpy = strdup(path); + name = strrchr(pathcpy, '/'); + if (name == &pathcpy[strlen(pathcpy) - 1]) { + /* get rid of the trailing slash */ + *name = '\0'; + name = strrchr(pathcpy, '/'); + } + if (name == NULL) { + name = pathcpy; + } else { + name++; + } + iso_tree_dir_set_name(dir, name); + free(pathcpy); + + + parent->nchildren++; + parent->children = realloc(parent->children, + parent->nchildren * sizeof(void*)); + parent->children[parent->nchildren - 1] = dir; + + return dir; +} + +struct iso_tree_dir *iso_tree_radd_dir(struct iso_tree_dir *parent, + const char *path) +{ + struct iso_tree_dir *nparent; + struct stat st; + int statret; + DIR *dir; + struct dirent *ent; + + assert( parent && path ); + statret = stat(path, &st); + + nparent = iso_tree_add_dir(parent, path); + + /* Open the directory for reading and iterate over the directory + entries. */ + dir = opendir(path); + + if (!dir) { + fprintf(stderr, "couldn't open directory %s: %s\n", + path, strerror(errno)); + return NULL; + } + + while ((ent = readdir(dir))) { + char *child; + + /* Skip current and parent directory entries. */ + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + + /* Build the child's full pathname. */ + child = iso_pathname(path, ent->d_name); + + /* Skip to the next entry on errors. */ + if (stat(child, &st) != 0) + continue; + + if (S_ISDIR(st.st_mode)) { + iso_tree_radd_dir(nparent, child); + } else { + iso_tree_add_file(nparent, child); + } + + free(child); + } + + closedir(dir); + + return nparent; +} + +struct iso_tree_dir *iso_tree_add_new_dir(struct iso_tree_dir *parent, + const char *name) +{ + struct iso_tree_dir *dir; + + assert( parent && name ); + + dir = calloc(1, sizeof(struct iso_tree_dir)); + dir->parent = parent; + dir->volume = parent->volume; + + iso_tree_dir_set_name(dir, name); + + dir->attrib = parent->attrib; + dir->attrib.st_mtime = time(NULL); + dir->attrib.st_atime = time(NULL); + dir->attrib.st_ctime = time(NULL); + + + /* add the new directory to parent */ + parent->nchildren++; + parent->children = realloc(parent->children, + parent->nchildren * sizeof(void*)); + parent->children[parent->nchildren - 1] = dir; + + return dir; +} + +void iso_tree_file_set_name(struct iso_tree_file *file, const char *name) +{ + file->name.full = strdup(name); + file->name.iso1 = iso_1_fileid(name); + file->name.iso2 = iso_2_fileid(name); + file->name.rockridge = iso_p_filename(name); + file->name.joliet = iso_j_id(file->name.full); +} + +char *iso_tree_node_get_name_nconst(const struct iso_tree_node *node, + enum iso_name_version ver) +{ + if (ver == ISO_NAME_ISO) { + if (node->volume->iso_level == 1) { + return node->name.iso1; + } else { + return node->name.iso2; + } + } + + switch (ver) { + case ISO_NAME_FULL: + return node->name.full; + case ISO_NAME_ISO: + if (node->volume->iso_level == 1) { + return node->name.iso1; + } else { + return node->name.iso2; + } + case ISO_NAME_ISO_L1: + return node->name.iso1; + case ISO_NAME_ISO_L2: + return node->name.iso2; + case ISO_NAME_ROCKRIDGE: + return node->name.rockridge; + case ISO_NAME_JOLIET: + return (char*) node->name.joliet; + } + + assert(0); + return NULL; /* just to shut up warnings */ +} + +const char *iso_tree_node_get_name(const struct iso_tree_node *node, + enum iso_name_version ver) +{ + return iso_tree_node_get_name_nconst(node, ver); +} + +void iso_tree_dir_set_name(struct iso_tree_dir *dir, const char *name) +{ + dir->name.full = strdup(name); + /* Level 1 directory is a string of d-characters of maximum size 8. */ + dir->name.iso1 = iso_d_str(name, 8, 0); + /* Level 2 directory is a string of d-characters of maximum size 31. */ + dir->name.iso2 = iso_d_str(name, 31, 0); + dir->name.rockridge = iso_p_dirname(name); + dir->name.joliet = iso_j_id(dir->name.full); +} + +/* Compares file names for use with qsort. */ +int iso_node_cmp(const void *v1, const void *v2) +{ + struct iso_tree_node **f1 = (struct iso_tree_node **)v1; + struct iso_tree_node **f2 = (struct iso_tree_node **)v2; + + return strcmp(iso_tree_node_get_name(*f1, ISO_NAME_FULL), + iso_tree_node_get_name(*f2, ISO_NAME_FULL)); +} + +int iso_node_cmp_iso(const void *v1, const void *v2) +{ + struct iso_tree_node **f1 = (struct iso_tree_node **)v1; + struct iso_tree_node **f2 = (struct iso_tree_node **)v2; + + return strcmp(iso_tree_node_get_name(*f1, ISO_NAME_ISO), + iso_tree_node_get_name(*f2, ISO_NAME_ISO)); +} + +int iso_node_cmp_joliet(const void *v1, const void *v2) +{ + struct iso_tree_node **f1 = (struct iso_tree_node **)v1; + struct iso_tree_node **f2 = (struct iso_tree_node **)v2; + + return ucscmp((uint16_t*)iso_tree_node_get_name(*f1, ISO_NAME_JOLIET), + (uint16_t*)iso_tree_node_get_name(*f2, ISO_NAME_JOLIET)); +} + +void iso_tree_sort(struct iso_tree_dir *root) +{ + int i; + + qsort(root->files, root->nfiles, sizeof(struct iso_tree_file *), + iso_node_cmp); + + qsort(root->children, root->nchildren, sizeof(struct iso_tree_dir *), + iso_node_cmp); + + for (i = 0; i < root->nchildren; i++) + iso_tree_sort(root->children[i]); +} + +void iso_tree_print(const struct iso_tree_dir *root, int spaces) +{ + iso_tree_print_verbose(root, NULL, NULL, NULL, spaces); +} + +void iso_tree_print_verbose(const struct iso_tree_dir *root, + print_dir_callback dc, print_file_callback fc, + void *data, int spaces) +{ + int i, j; + + for (i = 0; i < spaces; i++) + printf(" "); + + /* Root directory doesn't have a name. */ + if (root->name.full != NULL) + printf("%s", iso_tree_node_get_name(ISO_NODE(root), + ISO_NAME_ISO)); + printf("/\n"); + if (dc) dc(root, data, spaces); + + spaces += 2; + + for (j = 0; j < root->nchildren; j++) { + iso_tree_print_verbose(root->children[j], dc, fc, data, + spaces); + } + + for (j = 0; j < root->nfiles; j++) { + for (i = 0; i < spaces; i++) + printf(" "); + printf("%s\n", + iso_tree_node_get_name(ISO_NODE(root->files[j]), + ISO_NAME_ISO)); + if (fc) fc(root->files[j], data, spaces); + } +} diff --git a/libisofs/tree.h b/libisofs/tree.h new file mode 100755 index 0000000..34d9240 --- /dev/null +++ b/libisofs/tree.h @@ -0,0 +1,259 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file tree.h + * + * Extra declarations for use with the iso_tree_dir and iso_tree_file + * structures. + */ + +#ifndef __ISO_TREE +#define __ISO_TREE + +#include +#include +#include +#include + +#include "libisofs.h" + +/** + * File or directory names or identifiers. + */ +struct iso_names +{ + char *full; /**< Full version: either part of the path or + user input. */ + char *iso1; /**< ISO level 1 identifier. */ + char *iso2; /**< ISO level 2 identifier. */ + char *rockridge; /**< Rock Ridge file or directory name. */ + uint16_t *joliet; /**< Joliet identifier. */ +}; + +/** + * Directory on a volume. + */ +struct iso_tree_dir +{ + struct iso_tree_dir *parent; /**< \see iso_tree_node */ + struct iso_volume *volume; /**< \see iso_tree_node */ + struct iso_names name; /**< \see iso_tree_node */ + struct stat attrib; /**< \see iso_tree_node */ + off_t block; /**< \see iso_tree_node */ + uint8_t dirent_len; /**< \see iso_tree_node */ + void *writer_data; /**< \see iso_tree_node */ + + int depth; /**< The depth of this directory in the + * Directory Heirarchy. This is 1 for + * the root directory. + */ + + int nchildren; /**< Number of child directories. */ + int nfiles; /**< Number of files in this directory. + */ + struct iso_tree_dir **children; /**< Child directories. */ + struct iso_tree_file **files; /**< Files in this directory. */ +}; + +/** + * File on a volume. + */ +struct iso_tree_file +{ + struct iso_tree_dir *parent; /**< \see iso_tree_node */ + struct iso_volume *volume; /**< \see iso_tree_node */ + struct iso_names name; /**< \see iso_tree_node */ + struct stat attrib; /**< \see iso_tree_node */ + off_t block; /**< \see iso_tree_node */ + uint8_t dirent_len; /**< \see iso_tree_node */ + void *writer_data; /**< \see iso_tree_node */ + + char *path; /**< Location of the file in the + * local filesystem. This can be a + * full or relative path. If this is + * NULL, then the file doesn't exist + * in the local filesystem and its + * size must be zero. + * + * FIXME: Allow references to files + * on other volumes belonging to the + * same volset as this file. + */ +}; + +/** + * Fields in common between iso_tree_file and iso_tree_dir. + */ +struct iso_tree_node +{ + struct iso_tree_dir *parent; /**< The parent of this node. Must be + * non-NULL unless we are the + * root directory on a volume. + */ + struct iso_volume *volume; /**< The volume to which this node + * belongs. + */ + struct iso_names name; /**< The name of this node in its parent + * directory. Must be non-NULL unless + * we are the root directory on a + * volume. + */ + struct stat attrib; /**< The POSIX attributes of this + * node as documented in "man 2 stat". + * + * Any node that is not a regular + * file or a directory must have + * \p attrib.st_size set to zero. Any + * node that is a directory will have + * \p attrib.st_size filled out by the + * writer. + */ + + /* information used for writing */ + off_t block; /**< The block at which this node's + * data will be written. + */ + uint8_t dirent_len; /**< The size of this node's + * Directory Record in its parent. + * This does not include System Use + * fields, if present. + */ + void *writer_data; /**< This is writer-specific data. It + * must be set to NULL when a node + * is created and it should be NULL + * again when the node is freed. + */ +}; + +/** A macro to simplify casting between nodes and files/directories. */ +#define ISO_NODE(a) ( (struct iso_tree_node*) (a) ) + +/** A macro to simplify casting between nodes and directories. */ +#define ISO_DIR(a) ( (struct iso_tree_dir*) (a) ) + +/** A macro to simplify casting between nodes and files. */ +#define ISO_FILE(a) ( (struct iso_tree_file*) (a) ) + +/** + * Create a new root directory for a volume. + * + * \param volume The volume for which to create a new root directory. + * + * \pre \p volume is non-NULL. + * \post \p volume has a non-NULL, empty root directory. + * \return \p volume's new non-NULL, empty root directory. + */ +struct iso_tree_dir *iso_tree_new_root(struct iso_volume *volume); + +/** + * Create a new, empty, file. + * + * \param parent The parent of the new file. + * \param name The name of the new file, encoded in the current locale. + * + * \pre \p parent is non-NULL. + * \pre \p name is non-NULL and it does not match any other file or directory + * name in \p parent. + * \post \p parent contains a file with the following properties: + * - the file's (full) name is \p name + * - 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_file *iso_tree_add_new_file(struct iso_tree_dir *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_dir *root); + +/** + * Recursively sort all the files and child directories in a directory. + * + * \param root The root of the directory heirarchy to sort. + * + * \pre \p root is non-NULL. + * \post For each directory \p dir in the directory heirarchy descended fro + * root, the fields in \p dir.children and \p dir.files are alphabetically + * sorted according to \p name.full. + * + * \see iso_names + */ +void iso_tree_sort(struct iso_tree_dir *root); + +/** + * Compare the names of 2 nodes, \p *v1 and \p *v2. This is compatible with + * qsort. + */ +int iso_node_cmp(const void *v1, const void *v2); + +/** + * Compare the joliet names of 2 nodes, compatible with qsort. + */ +int iso_node_cmp_joliet(const void *v1, const void *v2); + +/** + * Compare the iso names of 2 nodes, compatible with qsort. + */ +int iso_node_cmp_iso(const void *v1, const void *v2); + +/** + * 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_dir *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_file *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_dir *root, + print_dir_callback dir, + print_file_callback file, + void *callback_data, + int spaces); + +/** + * Get a non-const version of the node name. This is used for name-mangling + * iso names. It should only be used internally in libisofs; all other users + * should only access the const name. + */ +char *iso_tree_node_get_name_nconst(const struct iso_tree_node *node, + enum iso_name_version ver); +#endif /* __ISO_TREE */ diff --git a/libisofs/util.c b/libisofs/util.c new file mode 100755 index 0000000..0295ac8 --- /dev/null +++ b/libisofs/util.c @@ -0,0 +1,710 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set ts=8 sts=8 sw=8 noet : */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +int valid_d_char(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_'); +} + +int valid_a_char(char c) +{ + return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?') || (c >= 'A' && + c <= 'Z') + || (c == '_'); +} + +int valid_j_char(char msb, char lsb) +{ + return !(msb == '\0' && + (lsb < ' ' || lsb == '*' || lsb == '/' || lsb == ':' || + lsb == ';' || lsb == '?' || lsb == '\\')); +} + +int valid_p_char(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && + c <= 'z') + || (c == '.') || (c == '_') || (c == '-'); +} + +char *iso_d_str(const char *src, int size, int pad) +{ + char *dest = strlen(src) > size || + pad ? malloc(size + 1) : malloc(strlen(src) + 1); + int i; + + /* Try converting to upper-case before validating. */ + for (i = 0; i < size && src[i]; i++) { + char c = toupper(src[i]); + + dest[i] = valid_d_char(c) ? c : '_'; + } + + /* Optionally pad with spaces and terminate with NULL. */ + if (pad) + while (i < size) + dest[i++] = ' '; + dest[i] = '\0'; + + return dest; +} + +char *iso_a_str(const char *src, int size) +{ + char *dest = malloc(size + 1); + int i; + + for (i = 0; i < size && src[i]; i++) { + char c = toupper(src[i]); + + dest[i] = valid_a_char(c) ? c : '_'; + } + while (i < size) + dest[i++] = ' '; + dest[i] = '\0'; + + return dest; +} + +uint16_t *iso_j_str(const char *src) +{ + int size = strlen(src) * 2; + uint16_t *dest = malloc(size + 2); + char *cpy, *in, *out; + size_t inleft, outleft; + iconv_t cd; + + /* If the conversion is unavailable, return NULL. Obviously, + nl_langinfo(..) requires the locale to be set correctly. */ + cd = iconv_open("UCS-2BE", nl_langinfo(CODESET)); + if (cd == (iconv_t) - 1) { + free(dest); + return NULL; + } + + /* In, out, inleft and outleft will be updated by iconv, that's why we + need separate variables. */ + cpy = strdup(src); /* iconv doesn't take const * chars, so we need our + * own copy. */ + in = cpy; + out = (char*)dest; + inleft = strlen(cpy); + outleft = size; + iconv(cd, &in, &inleft, &out, &outleft); + + /* Since we need to pad with NULLs, we can pad up to and including the + terminating NULLs. */ + outleft += 2; + while (outleft) { + *out++ = '\0'; + outleft--; + } + iconv_close(cd); + free(cpy); + + return dest; +} + +char *iso_1_fileid(const char *src) +{ + char *dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2 + (;1) + 1 (\0) */ + char *dot = strrchr(src, '.'); /* Position of the last dot in the + filename, will be used to calculate + lname and lext. */ + int lname, lext, pos, i; + + 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(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); + + return dest; +} + +char *iso_2_fileid(const char *src) +{ + char *dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2 + (;1) + 1 (\0) */ + char *dot = strrchr(src, '.'); + int lname, lext, lnname, lnext, pos, i; + + /* 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(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); + + return dest; +} + +uint16_t *iso_j_id(char *src) +{ + char *dest = malloc(136); /* 136 = 128 (name + ext) + 2 (\0 .) + + 4 (\0 ; \0 1) + 2 (\0 \0) */ + char *dot = strrchr(src, '.'); + int lname, lext, lcname, lcext, lnname, lnext, pos, i; + size_t inleft, outleft; + char *cname, *cext, *in, *out; + iconv_t cd; + + if (dot == NULL || dot == src || *(dot + 1) == '\0') { + lname = strlen(src); + lext = 0; + } else { + lext = strlen(dot + 1); + lname = strlen(src) - lext - 1; + } + + if (lname == 0 && lext == 0) { + free(dest); + return NULL; + } + + cd = iconv_open("UCS-2BE", nl_langinfo(CODESET)); + if (cd == (iconv_t) - 1) { + free(dest); + return NULL; + } + + /* We need to convert the name and extension first, in order to + calculate the number of characters they have. */ + cname = malloc(lname * 2); + in = src; + out = cname; + inleft = lname; + outleft = lname * 2; + iconv(cd, &in, &inleft, &out, &outleft); + lcname = (lname * 2) - outleft; + iconv_close(cd); + + if (lext) { + cd = iconv_open("UCS-2BE", nl_langinfo(CODESET)); + if (cd == (iconv_t) - 1) { + free(dest); + free(cname); + return NULL; + } + cext = malloc(lext * 2); + in = dot + 1; + out = cext; + inleft = lext; + outleft = lext * 2; + iconv(cd, &in, &inleft, &out, &outleft); + lcext = (lext * 2) - outleft; + iconv_close(cd); + } else + lcext = 0; + + /* Again, divide the available characters over name and extension, but + keep a minimum of three characters for the new extension. */ + lnext = (lcname + lcext > 128 && + lcext > 6) ? (lcname < 122 ? 128 - lcname : 6) : lcext; + lnname = (lcname + lcext > 128) ? 128 - lnext : lcname; + + pos = 0; + /* Copy up to lnname bytes from the converted filename. */ + for (i = 0; i < lnname; i = i + 2) + if (valid_j_char(cname[i], cname[i + 1])) { + dest[pos++] = cname[i]; + dest[pos++] = cname[i + 1]; + } else { + dest[pos++] = '\0'; + dest[pos++] = '_'; + } + /* Dot is now a 16 bit separator. */ + dest[pos++] = '\0'; + dest[pos++] = '.'; + /* Copy up to lnext bytes from the converted extension, if any. */ + for (i = 0; i < lnext; i = i + 2) + if (valid_j_char(cext[i], cext[i + 1])) { + dest[pos++] = cext[i]; + dest[pos++] = cext[i + 1]; + } else { + dest[pos++] = '\0'; + dest[pos++] = '_'; + } + /* Again, 2 bytes per character. */ + dest[pos++] = '\0'; + dest[pos++] = ';'; + dest[pos++] = '\0'; + dest[pos++] = '1'; + dest[pos++] = '\0'; + dest[pos] = '\0'; + + dest = (char *)realloc(dest, pos + 1); + free(cname); + if (lext) + free(cext); + + /* Fill in the size in bytes (including the terminating NULLs) of the + destination string. */ + return (uint16_t *) dest; +} + +char *iso_p_filename(const char *src) +{ + char *dest = malloc(251); /* We can fit up to 250 characters in + a Rock Ridge name entry. */ + char *dot = strrchr(src, '.'); + int lname, lext, lnname, lnext, pos, i; + + if (dot == NULL || dot == src || *(dot + 1) == '\0') { + lname = strlen(src); + lnname = (lname > 250) ? 250 : lname; + lext = lnext = 0; + } else { + lext = strlen(dot + 1); + lname = strlen(src) - lext - 1; + lnext = (strlen(src) > 250 && + lext > 3) ? (lname < 246 ? 249 - lname : 3) : lext; + lnname = (strlen(src) > 250) ? 249 - lnext : lname; + } + + if (lnname == 0 && lnext == 0) { + free(dest); + return NULL; + } + + pos = 0; + for (i = 0; i < lnname; i++) + dest[pos++] = valid_p_char(src[i]) ? src[i] : '_'; + if (lnext) { + dest[pos++] = '.'; + for (i = 0; i < lnext; i++) + dest[pos++] = + valid_p_char(src[lname + 1 + i]) ? + src[lname + 1 + i] : '_'; + } + dest[pos] = '\0'; + dest = (char *)realloc(dest, pos + 1); + + return dest; +} + +char *iso_p_dirname(const char *src) +{ + char *dest = strlen(src) > 250 ? malloc(251) : malloc(strlen(src) + 1); + int i; + + if (strlen(src) == 0) { + free(dest); + return NULL; + } + + for (i = 0; i < 250 && src[i]; i++) + dest[i] = valid_p_char(src[i]) ? src[i] : '_'; + dest[i] = '\0'; + + return dest; +} + +char *iso_pathname(const char *dir, const char *file) +{ + char *path = malloc(strlen(dir) + strlen(file) + 2); + + strcpy(path, dir); + path[strlen(dir)] = '/'; + strcpy(path + strlen(dir) + 1, file); + + return path; +} + +void iso_a_strcpy(unsigned char *dest, int size, const char *src) +{ + int i = 0, d = 0; + + if (src) + for (; i < size && *src; ++i, ++src) { + char s = toupper(*src); + + if (valid_a_char(s)) + dest[d++] = s; + else + dest[d++] = '_'; + } + for (; i < size; ++i) { + /* pad with spaces */ + dest[d++] = ' '; + } +} + +void iso_d_strcpy(unsigned char *dest, int size, const char *src) +{ + int i = 0, d = 0; + + if (src) + for (; i < size && *src; ++i, ++src) { + char s = toupper(*src); + + if (valid_d_char(s)) + dest[d++] = s; + else + dest[d++] = '_'; + } + for (; i < size; ++i) { + /* pad with spaces */ + dest[d++] = ' '; + } +} + +char *iso_a_strndup(const char *src, int size) +{ + int i, d; + char *out; + + out = malloc(size + 1); + for (i = d = 0; i < size && *src; ++i, ++src) { + char s = toupper(*src); + + if (valid_a_char(s)) + out[d++] = s; + else + out[d++] = '_'; + } + out[d++] = '\0'; + out = realloc(out, d); /* shrink the buffer to what we used */ + return out; +} + +char *iso_d_strndup(const char *src, int size) +{ + int i, d; + char *out; + + out = malloc(size + 1); + for (i = d = 0; i < size && *src; ++i, ++src) { + char s = toupper(*src); + + if (valid_d_char(s)) + out[d++] = s; + else + out[d++] = '_'; + } + out[d++] = '\0'; + out = realloc(out, d); /* shrink the buffer to what we used */ + return out; +} + +char *iso_strconcat(char sep, const char *a, const char *b) +{ + char *out; + int la, lb; + + la = strlen(a); + lb = strlen(b); + out = malloc(la + lb + 1 + (sep ? 1 : 0)); + memcpy(out, a, la); + memcpy(out + la + (sep ? 1 : 0), b, lb); + if (sep) + out[la] = sep; + out[la + lb + (sep ? 1 : 0)] = '\0'; + return out; +} + +char *iso_strdup(const char *src) +{ + return src ? strdup(src) : NULL; +} + +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; + static int tzoffset; + struct tm tm; + + if (!tzsetup) { + tzset(); + + tzoffset = -timezone / 60 / 15; + if (tzoffset < -48) + tzoffset += 101; + + 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; + 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; + char c[5]; + + if (t == (time_t) - 1) { + /* unspecified time */ + memset(buf, '0', 16); + buf[16] = 0; + } else { + if (!tzsetup) { + tzset(); + + tzoffset = -timezone / 60 / 15; + if (tzoffset < -48) + tzoffset += 101; + + tzsetup = 1; + } + + localtime_r(&t, &tm); + + /* year */ + sprintf(c, "%04d", tm.tm_year + 1900); + memcpy(&buf[0], c, 4); + /* month */ + sprintf(c, "%02d", tm.tm_mon + 1); + memcpy(&buf[4], c, 2); + /* day */ + sprintf(c, "%02d", tm.tm_mday); + memcpy(&buf[6], c, 2); + /* hour */ + sprintf(c, "%02d", tm.tm_hour); + memcpy(&buf[8], c, 2); + /* minute */ + sprintf(c, "%02d", tm.tm_min); + memcpy(&buf[10], c, 2); + /* second */ + sprintf(c, "%02d", MIN(59, tm.tm_sec)); + memcpy(&buf[12], c, 2); + /* hundreths */ + memcpy(&buf[14], "00", 2); + /* timezone */ + 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; +} + +void iso_split_filename(char *name, char **ext) +{ + char *r; + + r = strrchr(name, '.'); + if (r) { + *r = '\0'; + *ext = r + 1; + } else + *ext = ""; +} + +void iso_filecpy(unsigned char *buf, int size, const char *name, int version) +{ + char v[6]; + int nl, vl; + + assert(version >= 0); + assert(version < 0x8000); + + nl = strlen(name); + + memcpy(buf, name, nl); + + if (!version) + assert(size >= strlen(name)); + else { + sprintf(v, "%d", version); + vl = strlen(v); + assert(size >= nl + vl + 1); + + buf[nl] = ';'; + memcpy(&buf[nl + 1], v, vl); + + nl += vl + 1; + } + /* pad with spaces */ + if (nl < size) + memset(&buf[nl], ' ', size - nl); +} + +size_t ucslen(const uint16_t *str) +{ + int i; + + for (i=0; str[i]; i++) + ; + return i; +} + +int ucscmp(const uint16_t *s1, const uint16_t *s2) +{ + int i; + + for (i=0; 1; i++) { + if (s1[i] < s2[i]) { + return -1; + } else if (s1[i] > s2[i]) { + return 1; + } else if (s1[i] == 0 && s2[i] == 0) { + break; + } + } + + return 0; +} + +uint32_t iso_read_lsb(const uint8_t *buf, int bytes) +{ + int i; + uint32_t ret = 0; + + for (i=0; i + +/** + * Checks if the given character is a valid d-character. + */ +int valid_d_char(char c); + +/** + * Checks if the given character is a valid a-character. + */ +int valid_a_char(char c); + +/** + * Checks if the given character is part of the Joliet Allowed Character Set. + */ +int valid_j_char(char msb, char lsb); + +/** + * Checks if the given character is part of the POSIX Portable Filename Character Set. + */ +int valid_p_char(char c); + +/** + * Build a string of d-characters of maximum size 'size', optionally padded with spaces if necessary. + * Can be used to create directory identifiers if padding is turned off. + */ +char *iso_d_str(const char *src, int size, int pad); + +/** + * Build a string of a-characters of maximum size 'size', padded with spaces if necessary. + */ +char *iso_a_str(const char *src, int size); + +/** + * Build a string of j-characters of maximum size 'size', padded with NULLs if necessary. + * Requires the locale to be set correctly. + * @return NULL if the conversion from the current codeset to UCS-2BE is not available. + */ +uint16_t *iso_j_str(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(char *src); + +/** + * 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_filename(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_dirname(const char *src); + +/** + * Build a pathname out of a directory and file name. + */ +char *iso_pathname(const char *dir, const char *file); + +#include + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/** Copy a string of a-characters, padding the unused part of the + destination buffer */ +void iso_a_strcpy(unsigned char *dest, int size, const char *src); + +/** Copy a string of d-characters, padding the unused part of the + destination buffer */ +void iso_d_strcpy(unsigned char *dest, int size, const char *src); + +/** Returns a null terminated string containing the first 'size' a-characters + from the source */ +char *iso_a_strndup(const char *src, int size); + +/** Returns a null terminated string containing the first 'size' d-characters + from the source */ +char *iso_d_strndup(const char *src, int size); + +char *iso_strconcat(char sep, const char *a, const char *b); + +char *iso_strdup(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(unsigned char *buf, time_t t); + +/** Records the date/time into a 17 byte buffer (8.4.26.1) */ +void iso_datetime_17(unsigned char *buf, time_t t); + +time_t iso_datetime_read_7(const uint8_t *buf); +time_t iso_datetime_read_17(const uint8_t *buf); + +/** Removes the extension from the name, and returns the extension. The + returned extension should _not_ be freed! */ +void iso_split_filename(char *name, char **ext); + +void iso_filecpy(unsigned char *buf, int size, const char *name, int version); + +int iso_filename_len(const char *name, int iso_level); + +/* replacement for strlen for joliet names (wcslen doesn't work on machines + * with 32-bit wchar_t */ +size_t ucslen(const uint16_t *); + +/* replacement for strcmp for joliet names */ +int ucscmp(const uint16_t*, const uint16_t*); + +#define DIV_UP(n,div) ( ((n)+(div)-1) / (div) ) +#define ROUND_UP(n,mul) ( DIV_UP(n,mul) * (mul) ) + +#endif /* __ISO_UTIL */ diff --git a/libisofs/volume.c b/libisofs/volume.c new file mode 100755 index 0000000..15a86b4 --- /dev/null +++ b/libisofs/volume.c @@ -0,0 +1,72 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set ts=8 sts=8 sw=8 noet : */ + +#include + +#include "libisofs.h" +#include "tree.h" +#include "util.h" +#include "volume.h" +#include + +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->volume = malloc(sizeof(void *)); + volset->volume[0] = vol; + volset->volset_id.cstr = strdup(id); + volset->volset_id.jstr = iso_j_str(id); + return volset; +} + +struct iso_volume *iso_volume_new(const char *volume_id, + const char *publisher_id, + const char *data_preparer_id) +{ + struct iso_volume *volume; + + volume = calloc(1, sizeof(struct iso_volume)); + volume->refcount = 1; + + /* Get a new root directory. */ + volume->root = iso_tree_new_root(volume); + + /* Set these fields, if given. */ + if (volume_id != NULL) { + volume->volume_id.cstr = strdup(volume_id); + volume->volume_id.jstr = iso_j_str(volume_id); + } + if (publisher_id != NULL) { + volume->publisher_id.cstr = strdup(publisher_id); + volume->publisher_id.jstr = iso_j_str(publisher_id); + } + if (data_preparer_id != NULL) { + volume->data_preparer_id.cstr = strdup(data_preparer_id); + volume->data_preparer_id.jstr = iso_j_str(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.cstr); + free(volume->volume_id.jstr); + free(volume->publisher_id.cstr); + free(volume->publisher_id.jstr); + free(volume->data_preparer_id.cstr); + free(volume->data_preparer_id.jstr); + + free(volume); + } +} + +struct iso_tree_dir *iso_volume_get_root(const struct iso_volume *volume) +{ + return volume->root; +} diff --git a/libisofs/volume.h b/libisofs/volume.h new file mode 100755 index 0000000..db53aff --- /dev/null +++ b/libisofs/volume.h @@ -0,0 +1,55 @@ +/* -*- 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 __ISO_VOLUME +#define __ISO_VOLUME + +#include "libisofs.h" + +struct iso_string +{ + char *cstr; + uint16_t *jstr; +}; + +/** + * Data volume. + */ +struct iso_volume +{ + int refcount; /**< Number of used references to th + volume. */ + + struct iso_tree_dir *root; /**< Root of the directory tree for the + volume. */ + + unsigned rockridge:1; + unsigned joliet:1; + unsigned iso_level:2; + + struct iso_string volume_id; /**< Volume identifier. */ + struct iso_string publisher_id; /**< Volume publisher. */ + struct iso_string 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. */ + + struct iso_string volset_id; /**< The id of this volume set, encoded + in the current locale. */ +}; + +#endif /* __ISO_VOLUME */ diff --git a/libisofs/writer.c b/libisofs/writer.c new file mode 100755 index 0000000..2ed2e19 --- /dev/null +++ b/libisofs/writer.c @@ -0,0 +1,1178 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "writer.h" +#include "util.h" +#include "volume.h" +#include +#include +#include +#include +#include + +#define APPLICATION_ID "LIBBURN SUITE (C) 2006 M.DANIC/L.BIDDELL/A.NARAYANAN" + +enum DirType { + DIR_TYPE_SELF, + DIR_TYPE_PARENT, + DIR_TYPE_NORMAL, + DIR_TYPE_ROOT /* the dir record in the volume descriptor */ +}; + +enum SUSPType { + SUSP_TYPE_SP = 1 << 0, + SUSP_TYPE_CE = 1 << 1, + SUSP_TYPE_NM = 1 << 2 +}; + +static int get_path_table_size(struct iso_tree_dir **ddir); +static void iso_next_state(struct iso_write_target *target); +static int iso_source_get_size(struct burn_source *src); +static void iso_source_free(struct burn_source *src); +static void iso_target_layout(struct iso_write_target *target); +static void iso_dir_layout(struct iso_write_target *target, + struct iso_tree_dir **dir); +static void iso_file_layout(struct iso_write_target *target, + struct iso_tree_dir **dir); +static int iso_write_system_area(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int iso_write_pri_volume(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int iso_write_volume_terminator(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int write_path_table_record(unsigned char *buffer, + struct iso_tree_dir **ddir, int *position, + int lsb); +static int write_path_table_records(unsigned char *buffer, + struct iso_tree_dir **ddir, + int level, int *position, int lsb); +static int iso_write_path_table(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err, int lsb); +static int iso_write_dir_record(struct iso_write_target *target, + unsigned char *buffer, + struct iso_tree_dir **dir, enum DirType type); +static int iso_write_file_record(struct iso_write_target *t, + unsigned char *buffer, + struct iso_tree_file **ffile, + enum DirType type); +static void iso_write_file_id(unsigned char *buf, int size, + struct iso_tree_file **f); +static struct iso_tree_dir **find_dir_at_block(struct iso_tree_dir **ddir, + int block); +static struct iso_tree_file **find_file_at_block(struct iso_tree_dir **ddir, + int block); +static void write_child_records(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err, + struct iso_tree_dir *dir); +static int iso_write_dir_records(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int get_directory_record_length(const char *isoname); +static int iso_write_er_area(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); +static int copy_file_to_buffer(FILE * fd, unsigned char *buffer, int length); +static int iso_write_files(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err); + +int get_path_table_size(struct iso_tree_dir **ddir) +{ + const char *isoname; + int i, size = 0; + struct iso_tree_dir *dir = *ddir; + + assert(dir); + + isoname = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); + + if (isoname) { + /* a path table record is 8 bytes + the length of the + directory identifier */ + size += (8 + strlen(isoname)); + } else { + /* if iso_tree_dir_get_name is NULL, this is the root dir and + will have a path record of 10 bytes */ + size += 10; + } + + /* pad the field if the directory identifier is an odd number of + bytes */ + if (size % 2) + ++size; + + for (i = 0; i < dir->nchildren; i++) + size += get_path_table_size(dir->children[i].me); + + return size; +} + +struct burn_source *iso_source_new(struct iso_volumeset *volumeset, int volume) +{ + struct burn_source *src; + struct iso_write_target *t; + + if (!volumeset) + return NULL; + if (volume >= volumeset->volumeset_size) + return NULL; + + t = malloc(sizeof(struct iso_write_target)); + + iso_tree_sort(volumeset->root); + + t->volset = volumeset; + t->volset->refcount++; + t->volume = volume; + t->now = time(NULL); + t->phys_sector_size = 2048; + iso_target_layout(t); + + t->state = ISO_WRITE_BEFORE; + iso_next_state(t); /* prepare for the first state */ + + src = malloc(sizeof(struct burn_source)); + src->refcount = 1; + src->free_data = iso_source_free; + src->read = iso_source_generate; + src->read_sub = NULL; + src->get_size = iso_source_get_size; + src->data = t; + + return src; +} + +void iso_source_free(struct burn_source *src) +{ + struct iso_write_target *target = src->data; + + assert(src->read == iso_source_generate && + src->read_sub == NULL && src->get_size == iso_source_get_size); + + iso_volumeset_free(target->volset); + free(target); + + return; +} + +static int get_susp_length(struct RRInfo *info, enum SUSPType fields) +{ + int len = 0; + + if (fields & SUSP_TYPE_SP) + len += 7; + if (fields & SUSP_TYPE_CE) + len += 28; + if (info->px[0] != 0) + len += info->px[2]; + if (info->tf) + len += info->tf[2]; + if (info->nm && (fields & SUSP_TYPE_NM)) + len += info->nm[2]; + + return len; +} + +int get_directory_record_length(const char *isoname) +{ + int size; + + /* size = the length of the filename + 33 bytes (9.1) */ + size = 33 + strlen(isoname); + /* if size is odd, pad it with a single byte (9.1.12) */ + if (size % 2) + ++size; + + return size; +} + +/* fill in the 'logical_block' and 'size' values for each directory */ +void iso_dir_layout(struct iso_write_target *target, + struct iso_tree_dir **ddir) +{ + int i, length, sectors, rr; + const char *name; + struct iso_tree_dir *dir = *ddir; + + rr = iso_volumeset_get_rr(target->volset); + /* Each directory record starts with a record pointing to itself + and another pointing to its parent; combined, they are 68 bytes. + If RR is in use, calculate the susp length as well. */ + length = 68; + if (rr) { + /* if this is the root dir recored, we need to include the + length of the CE and SP fields */ + if (ddir == target->volset->root) { + length += get_susp_length(&dir->rr, SUSP_TYPE_SP | SUSP_TYPE_CE); + length += get_susp_length(&dir->rr, 0); + } else { + length += get_susp_length(&dir->rr, 0); + length += get_susp_length(&dir->parent->rr, 0); + } + } + + /* calculate the length of this directory */ + name = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); + if (name) + length += get_directory_record_length(name); + else + length += get_directory_record_length(""); + if (rr) + length += get_susp_length(&dir->rr, SUSP_TYPE_NM); + + for (i = 0; i < dir->nfiles; ++i) { + name = iso_tree_file_get_name(dir->files[i].me, + ISO_FILE_NAME_ISO); + length += get_directory_record_length(name); + if (rr) + length += get_susp_length(&dir->files[i].rr, SUSP_TYPE_NM); + } + + for (i = 0; i < dir->nchildren; ++i) { + name = iso_tree_dir_get_name(dir->children[i].me, + ISO_FILE_NAME_ISO); + length += get_directory_record_length(name); + if (rr) + length += get_susp_length(&dir->children[i].rr, SUSP_TYPE_NM); + } + + /* dir->size is the number of bytes needed to hold the directory + record for each file and subdirectory of 'dir', padded to use up + all of the bytes of a physical sector. */ + sectors = length / target->phys_sector_size; + if (length % target->phys_sector_size) + sectors++; + dir->size = sectors * target->phys_sector_size; + dir->logical_block = target->logical_block; + target->logical_block += sectors; + + /* now calculate the lengths of its children */ + for (i = 0; i < dir->nchildren; ++i) { + iso_dir_layout(target, dir->children[i].me); + } + + return; +} + +/* fill in the 'logical_block' and 'size' values for each file */ +void iso_file_layout(struct iso_write_target *target, + struct iso_tree_dir **ddir) +{ + int i, sectors; + struct iso_tree_dir *dir = *ddir; + + for (i = 0; i < dir->nfiles; ++i) { + dir->files[i].logical_block = target->logical_block; + sectors = dir->files[i].size / target->phys_sector_size; + if (dir->files[i].size % target->phys_sector_size) + sectors++; + /* ensure we move past this logical block if the file was empty */ + else if (!dir->files[i].size) + sectors++; + target->logical_block += sectors; + } + + for (i = 0; i < dir->nchildren; ++i) + iso_file_layout(target, dir->children[i].me); + + return; +} + +void iso_target_layout(struct iso_write_target *target) +{ + /* logical block numbering starts at 1, not 0 */ + target->logical_block = 1; + /* system area */ + target->logical_block += 16; + /* primary volume descriptor */ + target->logical_block++; + /* volume descriptor terminator */ + target->logical_block++; + + target->logical_block_size = 2048; + target->path_table_size = get_path_table_size(target->volset->root); + /* put a 1-block buffer before each path table */ + target->l_path_table_pos = 19; + target->logical_block += 2; + target->total_size += 2 * target->phys_sector_size; + /* put a 1-block buffer before each path table */ + target->m_path_table_pos = 21; + target->logical_block += 2; + + iso_dir_layout(target, target->volset->root); + /* iso_write_dir_records() puts a 1-block buffer after the directory + section, so increment this accordingly */ + target->logical_block++; + + /* the ce area only takes up one block */ + target->susp_er_pos = target->logical_block++; + + iso_file_layout(target, target->volset->root); + + target->volume_space_size = target->logical_block; + target->total_size = target->volume_space_size * target->phys_sector_size; + + return; +} + +int iso_source_get_size(struct burn_source *src) +{ + struct iso_write_target *target = src->data; + + assert(src->read == iso_source_generate && + src->read_sub == NULL && src->get_size == iso_source_get_size); + + return target->total_size; +} + +void iso_next_state(struct iso_write_target *target) +{ + switch (++target->state) { + case ISO_WRITE_BEFORE: + /* this should never occur */ + assert(0); + break; + case ISO_WRITE_SYSTEM_AREA: + target->state_data.system_area.sectors = 0; + break; + case ISO_WRITE_PRI_VOL_DESC: + target->total_size = target->logical_block - 1; + break; + case ISO_WRITE_VOL_DESC_TERMINATOR: + break; + case ISO_WRITE_L_PATH_TABLE: + target->state_data.path_tables.sectors = 0; + break; + case ISO_WRITE_M_PATH_TABLE: + target->state_data.path_tables.sectors = 0; + break; + case ISO_WRITE_DIR_RECORDS: + target->state_data.dir_records.sectors = 0; + break; + case ISO_WRITE_ER_AREA: + break; + case ISO_WRITE_FILES: + target->state_data.dir_records.sectors = 0; + break; + case ISO_WRITE_DONE: + break; + } + + return; +} + +int iso_source_generate(struct burn_source *src, unsigned char *buffer, + int size) +{ + int next = 0; + enum burn_source_status err = BURN_SOURCE_OK; + struct iso_write_target *target = src->data; + + assert(src->read == iso_source_generate && + src->read_sub == NULL && src->get_size == iso_source_get_size); + + /* make sure the app didn't fuck up badly. */ + if (size != target->phys_sector_size) + return BURN_SOURCE_FAILED; /* XXX better code */ + + /* make sure 'buffer' doesn't have anything in it */ + memset(buffer, 0, size); + + switch (target->state) { + case ISO_WRITE_BEFORE: + /* this should never occur */ + assert(0); + case ISO_WRITE_SYSTEM_AREA: + next = iso_write_system_area(target, buffer, &err); + break; + case ISO_WRITE_PRI_VOL_DESC: + /* set target->logical_block to the logical block containing + the root directory record */ + target->logical_block = 23; + next = iso_write_pri_volume(target, buffer, &err); + break; + case ISO_WRITE_VOL_DESC_TERMINATOR: + next = iso_write_volume_terminator(target, buffer, &err); + break; + case ISO_WRITE_L_PATH_TABLE: + next = iso_write_path_table(target, buffer, &err, 1); + break; + case ISO_WRITE_M_PATH_TABLE: + next = iso_write_path_table(target, buffer, &err, 0); + break; + case ISO_WRITE_DIR_RECORDS: + next = iso_write_dir_records(target, buffer, &err); + break; + case ISO_WRITE_ER_AREA: + next = iso_write_er_area(target, buffer, &err); + break; + case ISO_WRITE_FILES: + next = iso_write_files(target, buffer, &err); + break; + case ISO_WRITE_DONE: + err = BURN_SOURCE_EOF; + break; + } + + if (next) + iso_next_state(target); + + return err; +} + +/* writes 16 sectors of '0' */ +int iso_write_system_area(struct iso_write_target *t, + unsigned char *buffer, enum burn_source_status *err) +{ + struct iso_state_system_area *state = &t->state_data.system_area; + + memset(buffer, 0, t->phys_sector_size); + return (++state->sectors == 16); +} + +/* writes the primary volume descriptor */ +int iso_write_pri_volume(struct iso_write_target *t, + unsigned char *buffer, enum burn_source_status *err) +{ + /* volume descriptor type (8.4.1) */ + buffer[0] = 1; + /* standard identifier (8.4.2) */ + memcpy(&buffer[1], "CD001", 5); + /* volume descriptor version (8.4.3) */ + buffer[6] = 1; + /* unused field (8.4.4) */ + buffer[7] = 0; + /* system identifier (8.4.5) */ + /* XXX mkisofs puts "LINUX" here */ + memset(&buffer[8], ' ', 32); + /* volume identifier (8.4.6) */ + iso_d_strcpy(&buffer[40], 32, t->volset->volume_id[t->volume]); + /* unused field (8.4.7) */ + memset(&buffer[72], 0, 8); + /* volume space size (8.4.8) */ + iso_bb(&buffer[80], t->volume_space_size, 4); + /* unused field (8.4.9) */ + memset(&buffer[88], 0, 32); + /* volume set size (8.4.10) */ + iso_bb(&buffer[120], t->volset->volumeset_size, 2); + /* volume sequence number (8.4.11) */ + iso_bb(&buffer[124], t->volume + 1, 2); + /* logical block size (8.4.12) */ + iso_bb(&buffer[128], t->logical_block_size, 2); + /* path table size (8.4.13) */ + iso_bb(&buffer[132], t->path_table_size, 4); + /* location of occurance of type l path table (8.4.14) */ + iso_lsb(&buffer[140], t->l_path_table_pos, 4); + /* location of optional occurance of type l path table (8.4.15) */ + iso_lsb(&buffer[144], 0, 4); + /* location of occurance of type m path table (8.4.16) */ + iso_msb(&buffer[148], t->m_path_table_pos, 4); + /* location of optional occurance of type m path table (8.4.17) */ + iso_msb(&buffer[152], 0, 4); + /* directory record for root directory (8.4.18) */ + iso_write_dir_record(t, &buffer[156], t->volset->root, DIR_TYPE_ROOT); + /* volume set identifier (8.4.19) */ + iso_d_strcpy(&buffer[190], 128, t->volset->volumeset_id); + /* publisher identifier (8.4.20) */ + iso_a_strcpy(&buffer[318], 128, t->volset->publisher_id); + /* data preparer identifier (8.4.21) */ + iso_a_strcpy(&buffer[446], 128, t->volset->data_preparer_id); + /* application identifier (8.4.22) */ + iso_a_strcpy(&buffer[574], 128, APPLICATION_ID); + /* copyright file identifier (8.4.23) */ + iso_write_file_id(&buffer[702], 37, t->volset->copyright_file); + /* abstract file identifier (8.4.24) */ + iso_write_file_id(&buffer[739], 37, t->volset->abstract_file); + /* bibliographic file identifier (8.4.25) */ + iso_write_file_id(&buffer[776], 37, t->volset->bibliographic_file); + /* volume creation date and time (8.4.26) */ + /* XXX is this different for multisession? */ + iso_datetime_17(&buffer[813], t->now); + /* volume modification date and time (8.4.27) */ + iso_datetime_17(&buffer[830], t->now); + /* volume expiration date and time (8.4.28) */ + iso_datetime_17(&buffer[847], t->volset->expiration_time); + /* volume effective date and time (8.4.29) */ + iso_datetime_17(&buffer[864], + t->volset->effective_time == (time_t) - 1 ? + t->now : t->volset->effective_time); + /* file structure version (8.4.30) */ + buffer[881] = 1; + /* reserved for future standardization (8.4.31) */ + buffer[882] = 0; + /* application use (8.4.32) */ + /* XXX put something in here? does mkisofs? */ + memset(&buffer[883], 0, 512); + /* reserved for future standardization (8.4.33) */ + memset(&buffer[1395], 0, 653); + + return 1; +} + +void iso_write_file_id(unsigned char *buf, int size, struct iso_tree_file **f) +{ + if (!f) + memset(buf, ' ', size); + else + iso_filecpy(buf, size, + iso_tree_file_get_name(f, ISO_FILE_NAME_ISO), + (*f)->version); +} + +/* write the contents of the SP System Use Entry */ +/* SUSP-112 5.3 */ +static void fill_sp_sue(unsigned char *sp) +{ + sp[0] = 'S'; + sp[1] = 'P'; + sp[2] = 7; + sp[3] = 1; + sp[4] = 190; + sp[5] = 239; + sp[6] = 0; + + return; +} + +/* write the contents of the CE System Use Entry */ +/* SUSP-112 5.1 */ +static void fill_ce_sue(struct iso_write_target *t, + unsigned char *ce) +{ + ce[0] = 'C'; + ce[1] = 'E'; + ce[2] = 28; + ce[3] = 1; + iso_bb(&ce[4], 0, 4); + iso_bb(&ce[12], t->susp_er_pos, 4); + /* the length of the rock-ridge er area is 185, and that's all we + * store in the ce area right now */ + iso_bb(&ce[20], 185, 4); + + return; +} + +/* return a string containing the System Use Sharing Protocol information + * for 'ddir'. */ +static unsigned char* write_susp_info(struct iso_write_target *t, + struct RRInfo *rr, + enum SUSPType type, + int *len) +{ + unsigned char *buffer = NULL; + + *len = 0; + + /* check to see if susp is even needed (right now, only the rock-ridge + * extensions use it). */ + if (iso_volumeset_get_rr(t->volset)) { + int char_size, size = 0, offset = 0; + int len_sp, len_ce, len_px, len_tf, len_nm; + unsigned char *sp, *ce; + + char_size = sizeof (unsigned char *); + len_sp = len_ce = 0; + + if (type & SUSP_TYPE_SP) { + len_sp = 7; /* 7 bytes for the SP System Use Entry */ + sp = malloc(char_size * len_sp); + fill_sp_sue(sp); + size += len_sp; + } else { + sp = NULL; + } + + if (type & SUSP_TYPE_CE) { + len_ce = 28; /* 28 bytes for the CE System Use Entry */ + ce = malloc(char_size * len_ce); + fill_ce_sue(t, ce); + size += len_ce; + } else { + ce = NULL; + } + + if (rr->px[0] != 0) { + /* the 3rd byte of the PX field holds its length */ + len_px = rr->px[2]; + size += len_px; + } else { + len_px = 0; + } + + if (rr->tf) { + /* the 3rd byte of the TF field holds its length */ + len_tf = rr->tf[2]; + size += len_tf; + } else { + len_tf = 0; + } + + if ((type & SUSP_TYPE_NM) && rr->nm) { + /* the 3rd byte of the NM field holds its length */ + len_nm = rr->nm[2]; + size += len_nm; + } else { + len_nm = 0; + } + + /* fill 'buffer' with the susp info */ + buffer = malloc(char_size * (size + 1)); + if (sp) { + memcpy(buffer + offset, sp, len_sp); + offset += len_sp; + } + if (ce) { + memcpy(buffer + offset, ce, len_ce); + offset += len_ce; + } + if (len_px) { + memcpy(buffer + offset, rr->px, len_px); + offset += len_px; + } + if (len_tf) { + memcpy(buffer + offset, rr->tf, len_tf); + offset += len_tf; + } + if (len_nm) { + memcpy(buffer + offset, rr->nm, len_nm); + offset += len_nm; + } + buffer[offset] = '\0'; + *len = offset; + } + + return buffer; +} + +int iso_write_dir_record(struct iso_write_target *t, + unsigned char *buffer, + struct iso_tree_dir **ddir, enum DirType type) +{ + int len_fi, len_susp, sz; + const char *fi = NULL; + unsigned char *susp; /* the contents, if any, of the "system use sharing protocol" buffer */ + struct iso_tree_dir *dir = *ddir; + + if (type != DIR_TYPE_NORMAL) + len_fi = 1; + else { + assert(dir->parent); + fi = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); + len_fi = strlen(fi); + } + + /* determine if this is the root directory record */ + if ((*t->volset->root == dir) && (type == DIR_TYPE_SELF)) + susp = write_susp_info(t, &dir->rr, SUSP_TYPE_SP | SUSP_TYPE_CE, &len_susp); + /* determine if this a SELF or PARENT directory record */ + else if (type != DIR_TYPE_NORMAL) + susp = write_susp_info(t, &dir->rr, 0, &len_susp); + /* none of the above == a normal directory record */ + else + susp = write_susp_info(t, &dir->rr, SUSP_TYPE_NM, &len_susp); + + sz = 33 + len_fi + len_susp + !(len_fi % 2); + + /* length of directory record (9.1.1) */ + buffer[0] = sz; + /* extended attribute record length (9.1.2) */ + buffer[1] = 0; /* XXX allow for extended attribute records */ + /* location of extent (9.1.3) */ + iso_bb(&buffer[2], dir->logical_block, 4); + /* data length (9.1.4) */ + iso_bb(&buffer[10], dir->size, 4); + /* recording date and time (9.1.5) */ + iso_datetime_7(&buffer[18], t->now); + /* file flags (9.1.6) */ + buffer[25] = ISO_FILE_FLAG_DIRECTORY; + /* file unit size (9.1.7) */ + buffer[26] = 0; /* XXX support this shit? */ + /* interleave gap size (9.1.8) */ + buffer[27] = 0; /* XXX support this shit? */ + /* volume sequence number (9.1.9) */ + iso_bb(&buffer[28], dir->volume + 1, 2); + /* length of file identifier (9.1.10) */ + buffer[32] = len_fi; + /* file identifier */ + switch (type) { + case DIR_TYPE_ROOT: + case DIR_TYPE_SELF: + buffer[33] = 0; + break; + case DIR_TYPE_PARENT: + buffer[33] = 1; + break; + case DIR_TYPE_NORMAL: + iso_d_strcpy(&buffer[33], len_fi, fi); + break; + } + if (!(len_fi % 2)) + buffer[33 + len_fi] = 0; + /* system use sharing protocol */ + if (susp) + { + int offset; + if (!(len_fi % 2)) + offset = 34; + else + offset = 33; + memcpy(&buffer[offset + len_fi], susp, len_susp); + } + + return sz; +} + +/* write the file record as per (9.1) */ +int iso_write_file_record(struct iso_write_target *t, + unsigned char *buffer, + struct iso_tree_file **ffile, enum DirType type) +{ + int len_fi, len_susp; + const char *fi = NULL; + unsigned char *susp; /* the contents, if any, of the "system use sharing protocol" buffer */ + int sz; + struct iso_tree_file *file = *ffile; + + if (type != DIR_TYPE_NORMAL) + len_fi = 1; + else { + fi = iso_tree_file_get_name(ffile, ISO_FILE_NAME_ISO); + len_fi = strlen(fi); + } + + susp = write_susp_info(t, &file->rr, SUSP_TYPE_NM, &len_susp); + + sz = 33 + len_fi + len_susp + !(len_fi % 2); + + /* length of directory record (9.1.1) */ + buffer[0] = sz; + /* extended attribute record length (9.1.2) */ + buffer[1] = 0; /* XXX allow for extended attribute records */ + /* location of extent (9.1.3) */ + iso_bb(&buffer[2], file->logical_block, 4); + /* data length (9.1.4) */ + iso_bb(&buffer[10], file->size, 4); + /* recording date and time (9.1.5) */ + iso_datetime_7(&buffer[18], t->now); + /* file flags (9.1.6) */ + buffer[25] = ISO_FILE_FLAG_NORMAL; + /* file unit size (9.1.7) */ + buffer[26] = 0; /* XXX support this shit? */ + /* interleave gap size (9.1.8) */ + buffer[27] = 0; /* XXX support this shit? */ + /* volume sequence number (9.1.9) */ + iso_bb(&buffer[28], file->volume + 1, 2); + /* length of file identifier (9.1.10) */ + buffer[32] = len_fi; + /* file identifier */ + switch (type) { + case DIR_TYPE_ROOT: + case DIR_TYPE_SELF: + buffer[33] = 0; + break; + case DIR_TYPE_PARENT: + buffer[33] = 1; + break; + case DIR_TYPE_NORMAL: + memcpy(&buffer[33], fi, len_fi); + break; + } + if (!(len_fi % 2)) + buffer[33 + len_fi] = 0; + /* system use sharing protocol */ + if (susp) + { + int offset; + if (!(len_fi % 2)) + offset = 34; + else + offset = 33; + memcpy(&buffer[offset + len_fi], susp, len_susp); + } + + return sz; +} + +/* writes the volume descriptor set terminator */ +int iso_write_volume_terminator(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err) +{ + /* volume descriptor type (8.3.1) */ + buffer[0] = 255; + /* standard identifier (8.3.2) */ + memcpy(&buffer[1], "CD001", 5); + /* volume descriptor version (8.3.3) */ + buffer[6] = 1; + /* reserved for future standardization (8.3.4) */ + memset(&buffer[7], 0, 2041); + + return 1; +} + +/* write the path record for 'ddir' into 'buffer' */ +int write_path_table_record(unsigned char *buffer, + struct iso_tree_dir **ddir, int *position, int lsb) +{ + int len, bytes_written; + short parent_position; + const char *name; + struct iso_tree_dir *dir = *ddir; + + /* set the position in the path table of this directory */ + dir->position = *position; + /* increment the position for the next path table record */ + *position += 1; + if (dir->parent) + parent_position = dir->parent->position; + else + parent_position = 1; + + name = iso_tree_dir_get_name(ddir, ISO_FILE_NAME_ISO); + if (name) + len = strlen(name); + else + len = 1; + + /* the directory identifier length (9.4.1) */ + buffer[0] = len; + /* the extended attribute record length (9.4.2) */ + buffer[1] = 0; + /* location of extent (9.4.3) */ + if (lsb) + iso_lsb(&buffer[2], dir->logical_block, 4); + else + iso_msb(&buffer[2], dir->logical_block, 4); + /* parent directory record (9.4.4) */ + if (lsb) + iso_lsb(&buffer[6], parent_position, 2); + else + iso_msb(&buffer[6], parent_position, 2); + /* the directory identifier (9.4.5) */ + if (name) + memcpy(&buffer[8], name, len); + else + memset(&buffer[8], 0, len); + /* padding field (9.4.6) */ + if (len % 2) { + bytes_written = 9 + len; + buffer[bytes_written] = 0; + } else + bytes_written = 8 + len; + + return bytes_written; +} + +/* recursive function used to write the path records for each level + * of the file system sequentially, i.e. root, then all children of + * root, then all grandchildren of root, then all great-grandchildren + * of root, etc. */ +int write_path_table_records(unsigned char *buffer, + struct iso_tree_dir **ddir, + int level, int *position, int lsb) +{ + int i, offset; + struct iso_tree_dir *dir = *ddir; + + /* ISO9660 only allows directories to be nested 8-deep */ + if (level >= 8) + return 0; + + if (!level) { + offset = write_path_table_record(buffer, ddir, position, lsb); + } else { + offset = 0; + for (i = 0; i < dir->nchildren; ++i) { + offset += write_path_table_records(buffer + offset, + dir->children[i].me, + level - 1, + position, lsb); + } + } + + return offset; +} + +/* writes set of path table records */ +int iso_write_path_table(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err, int lsb) +{ + int i, offset, position; + struct iso_state_path_tables *state = &t->state_data.path_tables; + + if (state->sectors) { + offset = 0; + position = 1; + + for (i = 0; i < 8; ++i) { + offset += write_path_table_records(buffer + offset, + t->volset->root, + i, &position, lsb); + } + } else { + /* empty buffer sector */ + memset(buffer, 0, t->phys_sector_size); + } + + return (++state->sectors == 2); +} + +struct iso_tree_dir **find_dir_at_block(struct iso_tree_dir **ddir, int block) +{ + int i; + struct iso_tree_dir *dir = *ddir; + struct iso_tree_dir **to_write = NULL; + + if (dir->logical_block == block) + to_write = ddir; + + for (i = 0; (i < dir->nchildren) && !to_write; ++i) { + to_write = find_dir_at_block(dir->children[i].me, block); + } + + return to_write; +} + +struct iso_tree_file **find_file_at_block(struct iso_tree_dir **ddir, int block) +{ + int i; + struct iso_tree_dir *dir = *ddir; + struct iso_tree_file **to_write = NULL; + + for (i = 0; (i < dir->nfiles) && !to_write; ++i) { + if (dir->files[i].logical_block == block) + to_write = dir->files[i].me; + } + + for (i = 0; (i < dir->nchildren) && !to_write; ++i) { + to_write = find_file_at_block(dir->children[i].me, block); + } + + return to_write; +} + +/* write the records for children of 'dir' */ +void write_child_records(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err, struct iso_tree_dir *dir) +{ + int file_counter, dir_counter, order, offset; + + file_counter = dir_counter = offset = 0; + + while ((file_counter < dir->nfiles) || (dir_counter < dir->nchildren)) { + /* if there are files and dirs, compare them to figure out + which comes 1st alphabetically */ + if ((file_counter < dir->nfiles) && + (dir_counter < dir->nchildren)) { + const char *dirname, *filename; + + dirname = iso_tree_dir_get_name + (dir->children[dir_counter].me, + ISO_FILE_NAME_ISO); + filename = iso_tree_file_get_name + (dir->files[file_counter].me, + ISO_FILE_NAME_ISO); + order = strcmp(dirname, filename); + } else { + if (file_counter < dir->nfiles) + /* only files are left */ + order = 1; + else + /* only dirs are left */ + order = -1; + } + + if (order < 0) { + offset += iso_write_dir_record(t, + buffer + offset, + dir-> + children[dir_counter]. + me, DIR_TYPE_NORMAL); + dir_counter++; + } else { + offset += iso_write_file_record(t, + buffer + offset, + dir-> + files[file_counter].me, + DIR_TYPE_NORMAL); + file_counter++; + } + + } + + return; +} + +/* write out the next directory record */ +int iso_write_dir_records(struct iso_write_target *t, + unsigned char *buffer, enum burn_source_status *err) +{ + int finished, offset; + struct iso_tree_dir **ddir, *dir; + struct iso_state_dir_records *state = &t->state_data.dir_records; + + finished = 0; /* set to 1 when all directory records have + been written */ + + if (state->sectors++) { + /* t->logical_block is the next block to write. find the dir + record which belongs there and write it out. if none + exists, we're done writing the directory records. */ + + ddir = find_dir_at_block(t->volset->root, t->logical_block); + + if (ddir) { + /* 1) write the record for this directory 2) write the + record for the parent directory 3) write the + records for all child files and directories */ + dir = *ddir; + offset = iso_write_dir_record(t, + buffer, + ddir, DIR_TYPE_SELF); + if (!dir->parent) { + /* this is the root directory */ + offset += iso_write_dir_record(t, + buffer + offset, + ddir, + DIR_TYPE_PARENT); + } else { + offset += iso_write_dir_record(t, + buffer + offset, + dir->parent->me, + DIR_TYPE_PARENT); + } + + write_child_records(t, buffer + offset, err, dir); + + /* dir->size should always be a multiple of + t->phys_sector_size */ + t->logical_block += (dir->size / t->phys_sector_size); + } else { + /* we just wrote out 2048 0's, so we still need to + increment this */ + t->logical_block++; + finished = 1; + } + } + + return finished; +} + +/* write the 'ER' area of the disc (RRIP-112: 4.3) */ +int iso_write_er_area(struct iso_write_target *t, + unsigned char *buffer, + enum burn_source_status *err) +{ + if (iso_volumeset_get_rr(t->volset)) { + char *text; + int len; + + buffer[0] = 'E'; + buffer[1] = 'R'; + buffer[2] = 237; + buffer[3] = 1; + buffer[4] = 10; + buffer[5] = 84; + buffer[6] = 135; + buffer[7] = 1; + text = strdup("" + "RRIP_1991ATHE ROCK RIDGE INTERCHANGE PROTOCOL " + "PROVIDES SUPPORT FOR POSIX FILE SYSTEM " + "SEMANTICSPLEASE CONTACT DISC PUBLISHER FOR " + "SPECIFICATION SOURCE. SEE PUBLISHER IDENTIFIER IN " + "PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION"); + len = strlen (text); + + memcpy (buffer + 8, text, len); + free (text); + + /* next logical block... */ + t->logical_block++; + } + + return 1; +} + +int copy_file_to_buffer(FILE * fd, unsigned char *buffer, int length) +{ + int read; + + read = fread(buffer, 1, length, fd); + + return read; +} + +int iso_write_files(struct iso_write_target *t, + unsigned char *buffer, enum burn_source_status *err) +{ + FILE *fd; + int finished, sectors; + struct iso_tree_file **ffile, *file; + struct iso_state_files *state = &t->state_data.files; + + finished = 0; + + if (state->sectors) { + /* continue writing the file at 't->logical_block' to + 'buffer', then incremenet 't->logical_block' to the next + file section. */ + + fd = t->current_fd; + file = *(t->current_file); + + /* copy one sector of 'ffile' into 'buffer' */ + state->sectors += + copy_file_to_buffer(fd, buffer, t->phys_sector_size); + + if (feof(fd)) { + fclose(fd); + state->sectors = 0; + sectors = file->size / t->phys_sector_size; + if (file->size % t->phys_sector_size) + sectors++; + t->logical_block += sectors; + } + } else { + /* start writing the file at 't->logical_block' to 'buffer'. + if none exists, we are done. */ + ffile = find_file_at_block(t->volset->root, t->logical_block); + if (ffile) { + t->current_file = ffile; + file = *ffile; + fd = fopen(file->path, "r"); + t->current_fd = fd; + + if (fd) { + state->sectors += copy_file_to_buffer(fd, + buffer, + t-> + phys_sector_size); + } + + if (feof(fd)) { + fclose(fd); + fd = NULL; + } + + if (!fd) { + state->sectors = 0; + sectors = file->size / t->phys_sector_size; + if (file->size % t->phys_sector_size) + sectors++; + /* ensure we move past this logical block if the file was empty */ + else if (!file->size) + sectors++; + t->logical_block += sectors; + } + } else + finished = 1; + } + + return finished; +} diff --git a/libisofs/writer.h b/libisofs/writer.h new file mode 100755 index 0000000..018fd01 --- /dev/null +++ b/libisofs/writer.h @@ -0,0 +1,103 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef __WRITER +#define __WRITER + +#include "libisofs.h" + +#include +#include +#include + +enum iso_write_state +{ + ISO_WRITE_BEFORE, + + ISO_WRITE_SYSTEM_AREA, + ISO_WRITE_PRI_VOL_DESC, + ISO_WRITE_VOL_DESC_TERMINATOR, + ISO_WRITE_L_PATH_TABLE, + ISO_WRITE_M_PATH_TABLE, + ISO_WRITE_DIR_RECORDS, + ISO_WRITE_ER_AREA, + ISO_WRITE_FILES, + + ISO_WRITE_DONE +}; + +/** File Flags (9.1.6) */ +enum +{ + ISO_FILE_FLAG_NORMAL = 0, + ISO_FILE_FLAG_HIDDEN = 1 << 0, + ISO_FILE_FLAG_DIRECTORY = 1 << 1, + ISO_FILE_FLAG_ASSOCIATED = 1 << 2, + ISO_FILE_FLAG_RECORD = 1 << 3, + ISO_FILE_FLAG_PROTECTION = 1 << 4, + ISO_FILE_FLAG_MULTIEXTENT = 1 << 7 +}; + +struct iso_write_target +{ + struct iso_volumeset *volset; + int volume; + + /* the time at which the writing began */ + time_t now; + + /* size of a physical sector on the target disc */ + int phys_sector_size; + /* size of the total output */ + int total_size; + + /* when compiling the iso, this is the next available logical block. + when writing the iso, this is the next block to write. */ + int logical_block; + /* The number of Logical Blocks for the Volume Space */ + int volume_space_size; + /* The Logical Block size */ + int logical_block_size; + /* The Path Table size */ + int path_table_size; + /* Locations of Type L Path Table (Logical Block Number) */ + int l_path_table_pos; + /* Locations of Type M Path Table (Logical Block Number) */ + int m_path_table_pos; + /* Location of the SUSP ER area (Logical Block Number) */ + int susp_er_pos; + /* Current file being written in iso_write_files() */ + struct iso_tree_file **current_file; + FILE *current_fd; + + /* what we're doing when the generate function gets called next */ + enum iso_write_state state; + union + { + struct iso_state_system_area + { + /* how many sectors in the system area have been + written */ + int sectors; + } system_area; + struct iso_state_path_tables + { + /* how many sectors in the path table area have been + written */ + int sectors; + } path_tables; + struct iso_state_dir_records + { + /* how many sectors in the directory records area have + been written */ + int sectors; + } dir_records; + struct iso_state_files + { + /* how many sectors in the current file have been + written */ + int sectors; + } files; + } state_data; +}; + +#endif /* __WRITER */ 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/blank.c b/test/blank.c new file mode 100644 index 0000000..5f29443 --- /dev/null +++ b/test/blank.c @@ -0,0 +1,100 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include + +#include +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +static void blank_disc(struct burn_drive *drive) +{ + enum burn_disc_status s; + struct burn_progress p; + + if (!burn_drive_grab(drive, 1)) { + fprintf(stderr, "Unable to open the drive!\n"); + return; + } + + while (burn_drive_get_status(drive, NULL)) { + usleep(1000); + } + + while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(1000); + printf("%d\n", s); + if (s != BURN_DISC_FULL) { + burn_drive_release(drive, 0); + fprintf(stderr, "No disc found!\n"); + return; + } + + fprintf(stderr, "Blanking disc..."); + burn_disc_erase(drive, 1); + + while (burn_drive_get_status(drive, &p)) { + printf("%d\n", p.sector); + sleep(2); + } + fprintf(stderr, "Done\n"); + + burn_drive_release(drive, 0); +} + +void parse_args(int argc, char **argv, int *drive) +{ + int i; + int help = 0; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--drive")) { + ++i; + if (i >= argc) + printf("--drive requires an argument\n"); + else + *drive = atoi(argv[i]); + } else if (!strcmp(argv[i], "--verbose")) { + ++i; + if (i >= argc) + printf("--verbose requires an argument\n"); + else + burn_set_verbosity(atoi(argv[i])); + } else if (!strcmp(argv[i], "--help")) { + help = 1; + } + } + if (help) { + printf("Usage: %s [--drive ] [--verbose ]\n", + argv[0]); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) +{ + int drive = 0; + + parse_args(argc, argv, &drive); + + fprintf(stderr, "Initializing library..."); + if (burn_initialize()) + fprintf(stderr, "Success\n"); + else { + printf("Failed\n"); + return 1; + } + + fprintf(stderr, "Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + fprintf(stderr, "Done\n"); + + blank_disc(drives[drive].drive); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/test/burn.c b/test/burn.c new file mode 100644 index 0000000..a46866d --- /dev/null +++ b/test/burn.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/test/burniso.c b/test/burniso.c new file mode 100644 index 0000000..b0c6c4c --- /dev/null +++ b/test/burniso.c @@ -0,0 +1,147 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +void burn_iso(struct burn_drive *drive, const char *path, off_t size) +{ + struct burn_source *src; + struct burn_disc *disc; + struct burn_session *session; + struct burn_write_opts *o; + enum burn_disc_status s; + struct burn_track *tr; + struct burn_progress p; + + disc = burn_disc_create(); + session = burn_session_create(); + burn_disc_add_session(disc, session, BURN_POS_END); + tr = burn_track_create(); + burn_track_define_data(tr, 0, 0, 0, BURN_MODE1); + if (path[0] == '-' && path[1] == 0) { + src = burn_fd_source_new(0, -1, size); + printf("Note: using standard input as source with %.f bytes\n", + (double) size); + } else + src = burn_file_source_new(path, NULL); + assert(src); + + if (burn_track_set_source(tr, src) != BURN_SOURCE_OK) { + printf("problem with the source\n"); + return; + } + burn_session_add_track(session, tr, BURN_POS_END); + burn_source_free(src); + + if (!burn_drive_grab(drive, 1)) { + printf("Unable to open the drive!\n"); + return; + } + while (burn_drive_get_status(drive, NULL)) + usleep(1000); + + while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(1000); + + if (s != BURN_DISC_BLANK) { + burn_drive_release(drive, 0); + printf("put a blank in the drive, corky\n"); + return; + } + o = burn_write_opts_new(drive); + burn_write_opts_set_perform_opc(o, 0); + burn_write_opts_set_write_type(o, BURN_WRITE_RAW, BURN_BLOCK_RAW96R); + burn_write_opts_set_simulate(o, 1); + + burn_structure_print_disc(disc); + burn_drive_set_speed(drive, 0, 0); + burn_disc_write(o, disc); + burn_write_opts_free(o); + + while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) ; + while (burn_drive_get_status(drive, &p)) { + printf("S: %d/%d ", p.session, p.sessions); + printf("T: %d/%d ", p.track, p.tracks); + printf("L: %d: %d/%d\n", p.start_sector, p.sector, + p.sectors); + sleep(2); + } + printf("\n"); + burn_drive_release(drive, 0); + burn_track_free(tr); + burn_session_free(session); + burn_disc_free(disc); +} + +void parse_args(int argc, char **argv, int *drive, char **iso, off_t *size) +{ + int i; + int help = 0; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--drive")) { + ++i; + if (i >= argc) + printf("--drive requires an argument\n"); + else + *drive = atoi(argv[i]); + } else if (!strcmp(argv[i], "--stdin_size")) { + ++i; + if (i >= argc) + printf("--stdin_size requires an argument\n"); + else + *size = atoi(argv[i]); + } else if (!strcmp(argv[i], "--verbose")) { + ++i; + if (i >= argc) + printf("--verbose requires an argument\n"); + else + burn_set_verbosity(atoi(argv[i])); + } else if (!strcmp(argv[i], "--help")) { + help = 1; + } else + *iso = argv[i]; + } + if (help || !*iso) { + printf("Usage: %s [--drive ] [--stdin_size ] [--verbose ] isofile|-\n", argv[0]); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) +{ + int drive = 0; + char *iso = NULL; + off_t stdin_size= 650*1024*1024; + + parse_args(argc, argv, &drive, &iso, &stdin_size); + + printf("Initializing library..."); + if (burn_initialize()) + printf("Success\n"); + else { + printf("Failed\n"); + return 1; + } + + printf("Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + printf("Done\n"); + + burn_iso(drives[drive].drive, iso, stdin_size); + + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/test/devices.c b/test/devices.c new file mode 100644 index 0000000..6f3fdd5 --- /dev/null +++ b/test/devices.c @@ -0,0 +1,39 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include + +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +static void show_devices() +{ + unsigned int i; + + fprintf(stderr, "Devices: (%u found)\n", n_drives); + for (i = 0; i < n_drives; ++i) { + fprintf(stderr, "%s - %s %s\n", drives[i].location, + drives[i].vendor, drives[i].product); + } +} + +int main() +{ + fprintf(stderr, "Initializing library..."); + if (burn_initialize()) + fprintf(stderr, "Success\n"); + else { + printf("Failed\n"); + return 1; + } + + fprintf(stderr, "Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + fprintf(stderr, "Done\n"); + + show_devices(); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/test/iso.c b/test/iso.c new file mode 100644 index 0000000..ff74099 --- /dev/null +++ b/test/iso.c @@ -0,0 +1,138 @@ +/* -*- 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/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 burn_source *src; + unsigned char buf[2048]; + FILE *fd; + int c; + int level=1, flags=0; + DIR *dir; + struct dirent *ent; + + 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 ("must pass directory to build iso from\n"); + usage(); + return 1; + } + if (argc < 3) { + printf ("must supply output file\n"); + usage(); + return 1; + } + fd = fopen(argv[optind+1], "w"); + if (!fd) { + perror("error opening output file"); + exit(1); + } + + volume = iso_volume_new( "VOLID", "PUBID", "PREPID" ); + volset = iso_volset_new( volume, "VOLSETID" ); + dir = opendir(argv[optind]); + if (!dir) { + perror("error opening input directory"); + exit(1); + } + + while ( (ent = readdir(dir)) ) { + struct stat st; + char *name; + + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { + continue; + } + + name = malloc(strlen(argv[optind]) + strlen(ent->d_name) + 2); + strcpy(name, argv[optind]); + strcat(name, "/"); + strcat(name, ent->d_name); + if (lstat(name, &st) == -1) { + fprintf(stderr, "error opening file %s: %s\n", + name, strerror(errno)); + exit(1); + } + + if (S_ISDIR(st.st_mode)) { + iso_tree_radd_dir(iso_volume_get_root(volume), name); + } else { + iso_tree_add_file(iso_volume_get_root(volume), name); + } + free(name); + } + + iso_tree_print(iso_volume_get_root(volume), 0); + + 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/master.c b/test/master.c new file mode 100644 index 0000000..fa6b8ff --- /dev/null +++ b/test/master.c @@ -0,0 +1,126 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +void burn_files(struct burn_drive *drive, struct burn_disc *disc) +{ + struct burn_write_opts *o; + enum burn_disc_status s; + + if (!burn_drive_grab(drive, 1)) { + printf("Unable to open the drive!\n"); + return; + } + while (burn_drive_get_status(drive, NULL)) + usleep(1000); + + while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + usleep(1000); + + if (s != BURN_DISC_BLANK) { + burn_drive_release(drive, 0); + printf("put a blank in the drive, corky\n"); + return; + } + o = burn_write_opts_new(drive); + burn_drive_set_speed(drive, 0, 2816); + burn_write_opts_set_perform_opc(o, 0); + burn_write_opts_set_write_type(o, BURN_WRITE_TAO, BURN_BLOCK_MODE1); + burn_write_opts_set_simulate(o, 1); +/* want failure on seggy while debugging :) */ + burn_write_opts_set_underrun_proof(o, 0); + burn_structure_print_disc(disc); + burn_disc_write(o, disc); + burn_write_opts_free(o); + + while (burn_drive_get_status(drive, NULL)) { + sleep(1); + } + printf("\n"); + burn_drive_release(drive, 0); + burn_disc_free(disc); +} + +void parse_args(int argc, char **argv, int *drive, struct burn_disc **disc) +{ + int i, tmode = BURN_AUDIO; + int help = 1; + struct burn_session *session; + struct burn_track *tr; + struct burn_source *src; + + *disc = burn_disc_create(); + session = burn_session_create(); + burn_disc_add_session(*disc, session, BURN_POS_END); + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--drive")) { + ++i; + if (i >= argc) + printf("--drive requires an argument\n"); + else + *drive = atoi(argv[i]); + } else if (!strcmp(argv[i], "--audio")) { + tmode = BURN_AUDIO; + } else if (!strcmp(argv[i], "--data")) { + tmode = BURN_MODE1; + } else if (!strcmp(argv[i], "--verbose")) { + ++i; + if (i >= argc) + printf("--verbose requires an argument\n"); + else + burn_set_verbosity(atoi(argv[i])); + } else if (!strcmp(argv[i], "--help")) { + help = 1; /* who cares */ + } else { + help = 0; + printf("%s is a track\n", argv[i]); + + tr = burn_track_create(); + src = burn_file_source_new(argv[i], NULL); + burn_track_set_source(tr, src); + burn_source_free(src); + burn_track_define_data(tr, 0, 0, 1, tmode); + burn_session_add_track(session, tr, BURN_POS_END); + } + } + if (help) { + printf("Usage: %s [--drive ] [--verbose ] files\n", + argv[0]); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) +{ + int drive = 0; + struct burn_disc *disc; + + parse_args(argc, argv, &drive, &disc); + printf("Initializing library..."); + if (burn_initialize()) + printf("Success\n"); + else { + printf("Failed\n"); + return 1; + } + + printf("Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + printf("Done\n"); + + burn_files(drives[drive].drive, disc); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/test/poll.c b/test/poll.c new file mode 100644 index 0000000..cd22f15 --- /dev/null +++ b/test/poll.c @@ -0,0 +1,78 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include "libburn/libburn.h" +#include "libburn/toc.h" +#include "libburn/mmc.h" + +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; +int NEXT; + +static void catch_int () +{ + NEXT = 1; +} + +static void poll_drive(int d) +{ + enum burn_disc_status s; + + fprintf(stderr, "polling disc in %s - %s:\n", + drives[d].vendor, drives[d].product); + + if (!burn_drive_grab(drives[d].drive, 1)) { + fprintf(stderr, "Unable to open the drive!\n"); + return; + } + + while (burn_drive_get_status(drives[d].drive, NULL)) + usleep(1000); + + while ((s = burn_disc_get_status(drives[d].drive)) + == BURN_DISC_UNREADY) + usleep(1000); + + while (NEXT == 0) { + sleep(2); + mmc_get_event(drives[d].drive); + } + burn_drive_release(drives[d].drive, 0); +} + +int main() +{ + int i; + struct sigaction newact; + struct sigaction oldact; + fprintf(stderr, "Initializing library..."); + if (burn_initialize()) + fprintf(stderr, "Success\n"); + else { + printf("Failed\n"); + return 1; + } + + fprintf(stderr, "Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + fprintf(stderr, "Done\n"); + if (!drives) { + printf("No burner found\n"); + return 1; + } + + newact.sa_handler = catch_int; + sigaction(SIGINT, &newact, &oldact); + for (i = 0; i < n_drives; i++) { + NEXT=0; + poll_drive(i); + } + sigaction(SIGINT, &oldact, NULL); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} diff --git a/test/rip.c b/test/rip.c new file mode 100644 index 0000000..0606cf4 --- /dev/null +++ b/test/rip.c @@ -0,0 +1,54 @@ +/* THIS IS NOT A PROPER EXAMPLE */ +#include +#include +#include +#include +#include +#include + +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +#warning this example is totally fried +int main() +{ +#if 0 + struct burn_drive *drive; + struct burn_read_opts o; + + burn_initialize(); + o.datafd = + open("/xp/burn/blah.data", O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR); + o.subfd = + open("/xp/burn/blah.sub", O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR); + o.raw = 1; + o.c2errors = 0; + o.subcodes_audio = 1; + o.subcodes_data = 1; + o.hardware_error_recovery = 1; + o.report_recovered_errors = 0; + o.transfer_damaged_blocks = 1; + o.hardware_error_retries = 1; + + printf("Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + printf("Done\n"); + drive = drives[0].drive; + burn_drive_set_speed(drive, 0, 0); + + if (!burn_drive_grab(drive, 1)) { + printf("Unable to open the drive!\n"); + return EXIT_FAILURE; + } + + while (burn_drive_get_status(drive, NULL)) + usleep(1000); + + burn_disc_read(drive, &o); +#endif + return EXIT_SUCCESS; +} diff --git a/test/structest.c b/test/structest.c new file mode 100644 index 0000000..defa274 --- /dev/null +++ b/test/structest.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int i; + const char *path; + struct burn_track *track; + struct burn_disc *disc; + struct burn_session *session; + struct burn_source *src; + + disc = burn_disc_create(); + session = burn_session_create(); + burn_disc_add_session(disc, session, BURN_POS_END); + + /* Define a source for all of the tracks */ + path = strdup("/etc/hosts"); + src = burn_file_source_new(path, NULL); + + /* Add ten tracks to a session */ + for (i = 0; i < 10; i++) { + track = burn_track_create(); + burn_session_add_track(session, track, 0); + if (burn_track_set_source(track, src) != BURN_SOURCE_OK) { + printf("problem with the source\n"); + return 0; + } + } + + /* Add ten tracks to a session */ + for (i = 0; i < 10; i++) { + track = burn_track_create(); + burn_session_add_track(session, track, 0); + if (burn_track_set_source(track, src) != BURN_SOURCE_OK) { + printf("problem with the source\n"); + return 0; + } + } + + /* Delete a session */ + burn_session_remove_track(session, track); + + burn_structure_print_disc(disc); + return EXIT_SUCCESS; +} diff --git a/test/toc.c b/test/toc.c new file mode 100644 index 0000000..1ec7bcf --- /dev/null +++ b/test/toc.c @@ -0,0 +1,103 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#include +#include +#include +#include +#include + +static struct burn_drive_info *drives; +static unsigned int n_drives; + +static void show_tocs() +{ + struct burn_session **sessions; + struct burn_track **tracks; + struct burn_disc *disc; + int nses, ntracks, hidefirst; + unsigned int i, j, k; + struct burn_toc_entry e; + enum burn_disc_status s; + + for (i = 0; i < n_drives; ++i) { + fprintf(stderr, "TOC for disc in %s - %s:\n", + drives[i].vendor, drives[i].product); + + if (!burn_drive_grab(drives[i].drive, 1)) { + fprintf(stderr, "Unable to open the drive!\n"); + continue; + } + + while (burn_drive_get_status(drives[i].drive, NULL)) + usleep(1000); + + while ((s = burn_disc_get_status(drives[i].drive)) + == BURN_DISC_UNREADY) + usleep(1000); + if (s != BURN_DISC_FULL) { + burn_drive_release(drives[i].drive, 0); + fprintf(stderr, "No disc found!\n"); + continue; + } + + disc = burn_drive_get_disc(drives[i].drive); + + sessions = burn_disc_get_sessions(disc, &nses); + for (k = 0; k < nses; ++k) { + tracks = burn_session_get_tracks(sessions[k], + &ntracks); + hidefirst = burn_session_get_hidefirst(sessions[k]); + if (hidefirst) + fprintf(stderr, + "track: GAP (%2d) lba: %9d (%9d) %02d:%02d:%02d adr: X control: X mode: %d\n", + k + 1, 0, 0, 0, 2, 0, + burn_track_get_mode(tracks[0])); + + for (j = !!hidefirst; j < ntracks; ++j) { + burn_track_get_entry(tracks[j], &e); + fprintf(stderr, + "track: %3d (%2d) lba: %9d (%9d) %02d:%02d:%02d " + "adr: %d control: %d mode: %d\n", + e.point, e.session, + burn_msf_to_lba(e.pmin, e.psec, + e.pframe), + burn_msf_to_lba(e.pmin, e.psec, + e.pframe) * 4, + e.pmin, e.psec, e.pframe, e.adr, + e.control, + burn_track_get_mode(tracks[j])); + } + burn_session_get_leadout_entry(sessions[k], &e); + fprintf(stderr, + "track:lout (%2d) lba: %9d (%9d) %02d:%02d:%02d " + "adr: %d control: %d mode: %d\n", + k + 1, burn_msf_to_lba(e.pmin, e.psec, + e.pframe), + burn_msf_to_lba(e.pmin, e.psec, + e.pframe) * 4, e.pmin, + e.psec, e.pframe, e.adr, e.control, -1); + } + burn_disc_free(disc); + burn_drive_release(drives[i].drive, 0); + } +} + +int main() +{ + fprintf(stderr, "Initializing library..."); + if (burn_initialize()) + fprintf(stderr, "Success\n"); + else { + printf("Failed\n"); + return 1; + } + + fprintf(stderr, "Scanning for devices..."); + while (!burn_drive_scan(&drives, &n_drives)) ; + fprintf(stderr, "Done\n"); + + show_tocs(); + burn_drive_info_free(drives); + burn_finish(); + return 0; +} 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/test/tree.pyc b/test/tree.pyc new file mode 100644 index 0000000000000000000000000000000000000000..efc06d94f69107d01a8b45c266b315ee011aa41b GIT binary patch literal 3547 zcmb_f+ioL85Un2H;#_v4up0~QE<}q~vIN8e@c=@Cy#zrdWTY4o5ebbP&zMYhGUH57 zAV|&&-oN0P2R?((;5YaIIH!7SPn>-r#>(_mcUM(cSDmVweERps>PNr*^?V@3uY&he zJoXSnAo5b=$Ps2rcyi=PVB{6HDiU}~R3)g$5S&#`CnDb;)g-9tQhl~mr^;Tt0o~n! zhp}2bFYvb5ld(u-WNc(&J~k>2nRYx@j}i~>K*k{n@Y>Kytx2|B`#T~{yZC;jkZXwQk!0m7qlyweG7MV z(;yshi$B`=9FP4NLk?U51o{^OKLBV&ky;@RkAPJC)#Mcb4Tvf@kLq$zz*29vJ7Bcw zU20Yj{tjXcH$4UpjYOVA09}=EgI|3pa)c+z(=vww@~S3tnTkTYE@S+uWWFL}V7V?6 z;2kR%ovx<*s8RD*xsD_AH3fDH2dwj6PHgDO_OCb>nGrC%2(_-oAhv~JKl!O2N5PKb zX6K3B=_dhR?F};UiUe8|hn8_ot)g3bxz|4iD1kS+!G`fri7ZWXjl=LFj&++s;Y4>9 zL24P$Q9lb4Cp9>U;sCRG9yP(x?Vp8TWLcWI6r3Gc66V^>8a=9P{?vvW25GwKaI2i7 zUf++BDEIwGpzSsWF%5IyYqIGEj zKT(5-SZ9S}{1`_v0~N1fMoql@n&ofrhdsGcoIp)_U#lW0z_11Qw6Il_%BU&PngB3R z=YRzZCW=CHekg~R5A>iWG;XR!ShT?kZqEOZLt3N>-qpnDv{`K&9P)my`04rky}kW| zDSntavE}A0h9opzBs4w@oX!0+B(dsG0lIwhDkpN zrV)#YOiNfjY!6a<4>hZpS$x!eG(I3O zxzcrKlbnTqNpl$E$bQGNx!?QdgU_g%eih@26V3Jg1QpBowLkep@*-H zMWE{D)V;b{I&N;fQnxNz(=2(D=F^&bb!)S^zFt!sxjVq3{s>O(pF1F2|3v3x^TcN! zzV}L>dFYem?hZKeVJg4CV-GN(Fr^Q*|3MN}j7c@GT~kgjr^NX2n5 z9jdI66L?!=OpJBQJ)~Up>FsMRp{?jFv+(9qSInbB!Rr~Zkk|{LC;wv0nFs3DF`70O zbv`X+V|nPk_AOvnQ$%-p+a)bt(7n%ti@acz3dsN0 zy!fA?&qy)e&!%pvo4XB{x3m>sI7CNkcW1cKX7$>y7WWyd_qqllP8`w{M{IUCU`8H= gZK0$%eDRzmLqZ6A&NS5j4Vw5EX?o4IX5)?azrfqz2mk;8 literal 0 HcmV?d00001 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@